IR: Fix ConstantExpr::replaceUsesOfWithOnConstant()

Change `ConstantExpr` to follow the model the other constants are using:
only malloc a replacement if it's going to be used.  This fixes a subtle
bug where if an API user had used `ConstantExpr::get()` already to
create the replacement but hadn't given it any users, we'd delete the
replacement.

This relies on r216015 to thread `OnlyIfReduced` through
`ConstantExpr::getWithOperands()`.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216016 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Duncan P. N. Exon Smith 2014-08-19 20:03:35 +00:00
parent e2215571e5
commit b03916a88b
3 changed files with 36 additions and 56 deletions

View File

@ -1142,12 +1142,6 @@ 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 <>

View File

@ -2857,63 +2857,26 @@ void ConstantExpr::replaceUsesOfWithOnConstant(Value *From, Value *ToV,
Constant *To = cast<Constant>(ToV);
SmallVector<Constant*, 8> NewOps;
unsigned NumUpdated = 0;
for (unsigned i = 0, e = getNumOperands(); i != e; ++i) {
Constant *Op = getOperand(i);
NewOps.push_back(Op == From ? To : Op);
if (Op == From) {
++NumUpdated;
Op = To;
}
NewOps.push_back(Op);
}
assert(NumUpdated && "I didn't contain From!");
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);
if (Constant *C = getWithOperands(NewOps, getType(), true)) {
replaceUsesOfWithOnConstantImpl(C);
return;
}
// Everyone using this now uses the replacement.
replaceAllUsesWith(Replacement);
// Delete the old constant!
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;
// Update to the new value.
if (Constant *C = getContext().pImpl->ExprConstants.replaceOperandsInPlace(
NewOps, this, From, To, NumUpdated, U - OperandList))
replaceUsesOfWithOnConstantImpl(C);
}
Instruction *ConstantExpr::getAsInstruction() {

View File

@ -299,5 +299,28 @@ TEST(ConstantsTest, ConstantArrayReplaceWithConstant) {
ASSERT_EQ(A01, RefArray->getInitializer());
}
TEST(ConstantsTest, ConstantExprReplaceWithConstant) {
LLVMContext Context;
std::unique_ptr<Module> M(new Module("MyModule", Context));
Type *IntTy = Type::getInt8Ty(Context);
Constant *G1 = new GlobalVariable(*M, IntTy, false,
GlobalValue::ExternalLinkage, nullptr);
Constant *G2 = new GlobalVariable(*M, IntTy, false,
GlobalValue::ExternalLinkage, nullptr);
ASSERT_NE(G1, G2);
Constant *Int1 = ConstantExpr::getPtrToInt(G1, IntTy);
Constant *Int2 = ConstantExpr::getPtrToInt(G2, IntTy);
ASSERT_NE(Int1, Int2);
GlobalVariable *Ref =
new GlobalVariable(*M, IntTy, false, GlobalValue::ExternalLinkage, Int1);
ASSERT_EQ(Int1, Ref->getInitializer());
G1->replaceAllUsesWith(G2);
ASSERT_EQ(Int2, Ref->getInitializer());
}
} // end anonymous namespace
} // end namespace llvm