Skip to content

[GVN] Improve processBlock for instruction erasure #131753

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 3 commits into from
May 6, 2025

Conversation

madhur13490
Copy link
Contributor

This patch deletes the instructions right away by using the approapriate iterators. Thus avoids collecting the instructions in a vector and then doing the erasure.

@llvmbot
Copy link
Member

llvmbot commented Mar 18, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Madhur Amilkanthwar (madhur13490)

Changes

This patch deletes the instructions right away by using the approapriate iterators. Thus avoids collecting the instructions in a vector and then doing the erasure.


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

2 Files Affected:

  • (modified) llvm/include/llvm/Transforms/Scalar/GVN.h (+6-3)
  • (modified) llvm/lib/Transforms/Scalar/GVN.cpp (+15-47)
diff --git a/llvm/include/llvm/Transforms/Scalar/GVN.h b/llvm/include/llvm/Transforms/Scalar/GVN.h
index c8be390799836..01b3763fb5581 100644
--- a/llvm/include/llvm/Transforms/Scalar/GVN.h
+++ b/llvm/include/llvm/Transforms/Scalar/GVN.h
@@ -25,6 +25,8 @@
 #include "llvm/IR/ValueHandle.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/Compiler.h"
+#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
+#include "llvm/Transforms/Utils/Local.h"
 #include <cstdint>
 #include <optional>
 #include <utility>
@@ -137,9 +139,11 @@ class GVNPass : public PassInfoMixin<GVNPass> {
 
   /// This removes the specified instruction from
   /// our various maps and marks it for deletion.
-  void markInstructionForDeletion(Instruction *I) {
+  void doInstructionDeletion(Instruction *I) {
+    salvageKnowledge(I, AC);
+    salvageDebugInfo(*I);
     VN.erase(I);
-    InstrsToErase.push_back(I);
+    removeInstruction(I);
   }
 
   DominatorTree &getDominatorTree() const { return *DT; }
@@ -306,7 +310,6 @@ class GVNPass : public PassInfoMixin<GVNPass> {
   // propagate to any successors. Entries added mid-block are applied
   // to the remaining instructions in the block.
   SmallMapVector<Value *, Value *, 4> ReplaceOperandsWithMap;
-  SmallVector<Instruction *, 8> InstrsToErase;
 
   // Map the block to reversed postorder traversal number. It is used to
   // find back edge easily.
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 85fcdd95f5d87..5550721f8d249 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -730,8 +730,9 @@ void GVNPass::ValueTable::erase(Value *V) {
 /// verifyRemoved - Verify that the value is removed from all internal data
 /// structures.
 void GVNPass::ValueTable::verifyRemoved(const Value *V) const {
-  assert(!valueNumbering.contains(V) &&
-         "Inst still occurs in value numbering map!");
+  if (V != nullptr)
+    assert(!valueNumbering.contains(V) &&
+           "Inst still occurs in value numbering map!");
 }
 
 //===----------------------------------------------------------------------===//
@@ -1574,7 +1575,7 @@ void GVNPass::eliminatePartiallyRedundantLoad(
     I->setDebugLoc(Load->getDebugLoc());
   if (V->getType()->isPtrOrPtrVectorTy())
     MD->invalidateCachedPointerInfo(V);
-  markInstructionForDeletion(Load);
+  doInstructionDeletion(Load);
   ORE->emit([&]() {
     return OptimizationRemark(DEBUG_TYPE, "LoadPRE", Load)
            << "load eliminated by PRE";
@@ -1798,7 +1799,7 @@ bool GVNPass::PerformLoadPRE(LoadInst *Load, AvailValInBlkVect &ValuesPerBlock,
       // Erase instructions generated by the failed PHI translation before
       // trying to number them. PHI translation might insert instructions
       // in basic blocks other than the current one, and we delete them
-      // directly, as markInstructionForDeletion only allows removing from the
+      // directly, as doInstructionDeletion only allows removing from the
       // current basic block.
       NewInsts.pop_back_val()->eraseFromParent();
     }
@@ -1997,7 +1998,7 @@ bool GVNPass::processNonLocalLoad(LoadInst *Load) {
         I->setDebugLoc(Load->getDebugLoc());
     if (V->getType()->isPtrOrPtrVectorTy())
       MD->invalidateCachedPointerInfo(V);
-    markInstructionForDeletion(Load);
+    doInstructionDeletion(Load);
     ++NumGVNLoad;
     reportLoadElim(Load, V, ORE);
     return true;
@@ -2067,7 +2068,7 @@ bool GVNPass::processAssumeIntrinsic(AssumeInst *IntrinsicI) {
       }
     }
     if (isAssumeWithEmptyBundle(*IntrinsicI)) {
-      markInstructionForDeletion(IntrinsicI);
+      doInstructionDeletion(IntrinsicI);
       return true;
     }
     return false;
@@ -2179,7 +2180,7 @@ bool GVNPass::processLoad(LoadInst *L) {
     return false;
 
   if (L->use_empty()) {
-    markInstructionForDeletion(L);
+    doInstructionDeletion(L);
     return true;
   }
 
@@ -2209,13 +2210,13 @@ bool GVNPass::processLoad(LoadInst *L) {
   // MaterializeAdjustedValue is responsible for combining metadata.
   ICF->removeUsersOf(L);
   L->replaceAllUsesWith(AvailableValue);
-  markInstructionForDeletion(L);
   if (MSSAU)
     MSSAU->removeMemoryAccess(L);
   ++NumGVNLoad;
   reportLoadElim(L, AvailableValue, ORE);
   // Tell MDA to reexamine the reused pointer since we might have more
   // information after forwarding it.
+  doInstructionDeletion(L);
   if (MD && AvailableValue->getType()->isPtrOrPtrVectorTy())
     MD->invalidateCachedPointerInfo(AvailableValue);
   return true;
@@ -2605,7 +2606,7 @@ bool GVNPass::processInstruction(Instruction *I) {
       Changed = true;
     }
     if (isInstructionTriviallyDead(I, TLI)) {
-      markInstructionForDeletion(I);
+      doInstructionDeletion(I);
       Changed = true;
     }
     if (Changed) {
@@ -2723,7 +2724,7 @@ bool GVNPass::processInstruction(Instruction *I) {
   patchAndReplaceAllUsesWith(I, Repl);
   if (MD && Repl->getType()->isPtrOrPtrVectorTy())
     MD->invalidateCachedPointerInfo(Repl);
-  markInstructionForDeletion(I);
+  doInstructionDeletion(I);
   return true;
 }
 
@@ -2799,10 +2800,6 @@ bool GVNPass::runImpl(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
 }
 
 bool GVNPass::processBlock(BasicBlock *BB) {
-  // FIXME: Kill off InstrsToErase by doing erasing eagerly in a helper function
-  // (and incrementing BI before processing an instruction).
-  assert(InstrsToErase.empty() &&
-         "We expect InstrsToErase to be empty across iterations");
   if (DeadBlocks.count(BB))
     return false;
 
@@ -2820,41 +2817,12 @@ bool GVNPass::processBlock(BasicBlock *BB) {
     VN.erase(PN);
     removeInstruction(PN);
   }
-
-  for (BasicBlock::iterator BI = BB->begin(), BE = BB->end();
-       BI != BE;) {
+  for (BasicBlock::iterator BI = BB->begin(), BE = BB->end(); BI != BE;) {
+    Instruction *Inst = &*BI++;
     if (!ReplaceOperandsWithMap.empty())
-      ChangedFunction |= replaceOperandsForInBlockEquality(&*BI);
-    ChangedFunction |= processInstruction(&*BI);
-
-    if (InstrsToErase.empty()) {
-      ++BI;
-      continue;
-    }
-
-    // If we need some instructions deleted, do it now.
-    NumGVNInstr += InstrsToErase.size();
-
-    // Avoid iterator invalidation.
-    bool AtStart = BI == BB->begin();
-    if (!AtStart)
-      --BI;
-
-    for (auto *I : InstrsToErase) {
-      assert(I->getParent() == BB && "Removing instruction from wrong block?");
-      LLVM_DEBUG(dbgs() << "GVN removed: " << *I << '\n');
-      salvageKnowledge(I, AC);
-      salvageDebugInfo(*I);
-      removeInstruction(I);
-    }
-    InstrsToErase.clear();
-
-    if (AtStart)
-      BI = BB->begin();
-    else
-      ++BI;
+      ChangedFunction |= replaceOperandsForInBlockEquality(Inst);
+    ChangedFunction |= processInstruction(Inst);
   }
-
   return ChangedFunction;
 }
 

@madhur13490
Copy link
Contributor Author

All tests pass but not sure how to construct a test for this patch. Suggestions welcome!

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

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

Could you provide more details on the motivation for this change? Is it just to resolve the FIXME or something else?

It looks like the stack-layout-remark.c clang test crashes.

@madhur13490
Copy link
Contributor Author

madhur13490 commented Mar 18, 2025

Could you provide more details on the motivation for this change? Is it just to resolve the FIXME or something else?

yes, primarily FIXME. Nothing else as such which motivates. The change is an optimization in the process of handling instructions by avoiding vector population just for the erasure.

It looks like the stack-layout-remark.c clang test crashes.

Fixed in the current revision. Thanks!

@madhur13490
Copy link
Contributor Author

@nikic Gentle ping!

@madhur13490
Copy link
Contributor Author

Ping to reviewers! @antoniofrighetto can you please have a look?

@@ -25,6 +25,8 @@
#include "llvm/IR/ValueHandle.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can remove this from the C++ file?

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

Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO it would be better to move the definition of doInstructionDeletion to the source file. There's no need to have it in the header.

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Can drop these includes 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.

Can't remove. salvage* is provided by AssumeBundleBuilder.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, but it's no longer necessary in the header. You can drop the includes in the header and restore the AssumeBundleBuilder.h include in the cpp file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You can drop the includes in the header

Do you mean just the AssumeBundleBuilder?

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean the two you added.

@@ -25,6 +25,8 @@
#include "llvm/IR/ValueHandle.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO it would be better to move the definition of doInstructionDeletion to the source file. There's no need to have it in the header.

Copy link

github-actions bot commented Apr 21, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@madhur13490
Copy link
Contributor Author

@antoniofrighetto @nikic Addressed all review comments. The thing about guarding assert under V != nullptr in verifyRemoved() was added for some test but I don't remember which one. Running ninja check-all today leads to no new failures, thus I have removed that change. Probably the issue is fixed somewhere else.

@madhur13490
Copy link
Contributor Author

Gentle ping @antoniofrighetto @nikic !

@@ -25,6 +25,8 @@
#include "llvm/IR/ValueHandle.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can drop these includes now.

This patch deletes the instructions right away by using
the approapriate iterators. Thus avoids collecting the instructions
in a vector and then doing the erasure.
@madhur13490
Copy link
Contributor Author

@nikic Removed the new includes from GVN.h and headers in GVN.cpp are untouched now.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

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

LGTM

@madhur13490 madhur13490 merged commit 015093d into llvm:main May 6, 2025
11 checks passed
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
This patch deletes the instructions immediately in core GVN processing by using the appropriate
iterators. Thus, it avoids collecting the instructions in a vector and then
doing the erasure.
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.

4 participants