IR: Reduce RAUW traffic in ConstantExpr
authorDuncan P. N. Exon Smith <dexonsmith@apple.com>
Tue, 19 Aug 2014 01:12:53 +0000 (01:12 +0000)
committerDuncan P. N. Exon Smith <dexonsmith@apple.com>
Tue, 19 Aug 2014 01:12:53 +0000 (01:12 +0000)
Avoid RAUW-ing `ConstantExpr` when an operand changes unless the new
`ConstantExpr` already has users.  This prevents the RAUW from rippling
up the expression tree unnecessarily.

This commit indirectly adds test coverage for r215953 (this is how I
came across the bug).

This is part of PR20515.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@215960 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/IR/Constants.h
lib/IR/Constants.cpp

index 31a0364b4b0960095694e93495ee42b31527bf95..ab692dcf3d6ae29ddad13b0171f920ebe9d17a16 100644 (file)
@@ -1107,6 +1107,12 @@ private:
   void setValueSubclassData(unsigned short D) {
     Value::setValueSubclassData(D);
   }
+
+  /// \brief Check whether this can become its replacement.
+  ///
+  /// For use during \a replaceUsesOfWithOnConstant(), check whether we know
+  /// how to turn this into \a Replacement, thereby reducing RAUW traffic.
+  bool canBecomeReplacement(const Constant *Replacement) const;
 };
 
 template <>
index c9ce2e826b920db5e9a0c8ff2d02697d8846fd7d..d2c7ceee116313fdc21646a8fb6bb5044a397325 100644 (file)
@@ -2836,6 +2836,25 @@ void ConstantExpr::replaceUsesOfWithOnConstant(Value *From, Value *ToV,
   Constant *Replacement = getWithOperands(NewOps);
   assert(Replacement != this && "I didn't contain From!");
 
+  // Check if Replacement has no users (and is the same type).  Ideally, this
+  // check would be done *before* creating Replacement, but threading this
+  // through constant-folding isn't trivial.
+  if (canBecomeReplacement(Replacement)) {
+    // Avoid unnecessary RAUW traffic.
+    auto &ExprConstants = getType()->getContext().pImpl->ExprConstants;
+    ExprConstants.remove(this);
+
+    auto *CE = cast<ConstantExpr>(Replacement);
+    for (unsigned I = 0, E = getNumOperands(); I != E; ++I)
+      // Only set the operands that have actually changed.
+      if (getOperand(I) != CE->getOperand(I))
+        setOperand(I, CE->getOperand(I));
+
+    CE->destroyConstant();
+    ExprConstants.insert(this);
+    return;
+  }
+
   // Everyone using this now uses the replacement.
   replaceAllUsesWith(Replacement);
 
@@ -2843,6 +2862,31 @@ void ConstantExpr::replaceUsesOfWithOnConstant(Value *From, Value *ToV,
   destroyConstant();
 }
 
+bool ConstantExpr::canBecomeReplacement(const Constant *Replacement) const {
+  // If Replacement already has users, use it regardless.
+  if (!Replacement->use_empty())
+    return false;
+
+  // Check for anything that could have changed during constant-folding.
+  if (getValueID() != Replacement->getValueID())
+    return false;
+  const auto *CE = cast<ConstantExpr>(Replacement);
+  if (getOpcode() != CE->getOpcode())
+    return false;
+  if (getNumOperands() != CE->getNumOperands())
+    return false;
+  if (getRawSubclassOptionalData() != CE->getRawSubclassOptionalData())
+    return false;
+  if (isCompare())
+    if (getPredicate() != CE->getPredicate())
+      return false;
+  if (hasIndices())
+    if (getIndices() != CE->getIndices())
+      return false;
+
+  return true;
+}
+
 Instruction *ConstantExpr::getAsInstruction() {
   SmallVector<Value*,4> ValueOperands;
   for (op_iterator I = op_begin(), E = op_end(); I != E; ++I)