Propagate ValueRanges across equality.

Add some more micro-optimizations: x * 0 = 0, a - x = a --> x = 0.


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@35138 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Nick Lewycky 2007-03-18 01:09:32 +00:00
parent 1cc6452181
commit 1eda0f60d7
2 changed files with 178 additions and 67 deletions

View File

@ -833,6 +833,10 @@ namespace {
return 0; return 0;
} }
#ifndef NDEBUG
bool isCanonical(Value *V, ETNode *Subtree, VRPSolver *VRP);
#endif
public: public:
bool isRelatedBy(Value *V1, Value *V2, ETNode *Subtree, LatticeVal LV) { bool isRelatedBy(Value *V1, Value *V2, ETNode *Subtree, LatticeVal LV) {
@ -893,10 +897,38 @@ namespace {
void addToWorklist(Value *V, const APInt *I, ICmpInst::Predicate Pred, void addToWorklist(Value *V, const APInt *I, ICmpInst::Predicate Pred,
VRPSolver *VRP); VRPSolver *VRP);
void mergeInto(Value **I, unsigned n, Value *New, ETNode *Subtree,
VRPSolver *VRP) {
assert(isCanonical(New, Subtree, VRP) && "Best choice not canonical?");
uint32_t W = widthOfValue(New);
if (!W) return;
ConstantRange CR_New = rangeFromValue(New, Subtree, W);
ConstantRange Merged = CR_New;
for (; n != 0; ++I, --n) {
ConstantRange CR_Kill = rangeFromValue(*I, Subtree, W);
if (CR_Kill.isFullSet()) continue;
Merged = Merged.intersectWith(CR_Kill);
}
if (Merged.isFullSet() || Merged == CR_New) return;
if (Merged.isSingleElement())
addToWorklist(New, Merged.getSingleElement(),
ICmpInst::ICMP_EQ, VRP);
else
update(New, Merged, Subtree);
}
void addInequality(Value *V1, Value *V2, ETNode *Subtree, LatticeVal LV, void addInequality(Value *V1, Value *V2, ETNode *Subtree, LatticeVal LV,
VRPSolver *VRP) { VRPSolver *VRP) {
assert(!isRelatedBy(V1, V2, Subtree, LV) && "Asked to do useless work."); assert(!isRelatedBy(V1, V2, Subtree, LV) && "Asked to do useless work.");
assert(isCanonical(V1, Subtree, VRP) && "Value not canonical.");
assert(isCanonical(V2, Subtree, VRP) && "Value not canonical.");
if (LV == NE) return; // we can't represent those. if (LV == NE) return; // we can't represent those.
// XXX: except in the case where isSingleElement and equal to either // XXX: except in the case where isSingleElement and equal to either
// Lower or Upper. That's probably not profitable. (Type::Int1Ty?) // Lower or Upper. That's probably not profitable. (Type::Int1Ty?)
@ -1150,7 +1182,7 @@ namespace {
// The first iteration through this loop operates on V2 before going // The first iteration through this loop operates on V2 before going
// through the Remove list and operating on those too. If all of the // through the Remove list and operating on those too. If all of the
// iterations performed simple replacements then we exit early. // iterations performed simple replacements then we exit early.
bool exitEarly = true; bool mergeIGNode = false;
unsigned i = 0; unsigned i = 0;
for (Value *R = V2; i == 0 || i < Remove.size(); ++i) { for (Value *R = V2; i == 0 || i < Remove.size(); ++i) {
if (i) R = IG.node(Remove[i])->getValue(); // skip n2. if (i) R = IG.node(Remove[i])->getValue(); // skip n2.
@ -1206,64 +1238,98 @@ namespace {
// If we make it to here, then we will need to create a node for N1. // If we make it to here, then we will need to create a node for N1.
// Otherwise, we can skip out early! // Otherwise, we can skip out early!
exitEarly = false; mergeIGNode = true;
} }
if (exitEarly) return true; if (!isa<Constant>(V1)) {
if (Remove.empty()) {
VR.mergeInto(&V2, 1, V1, Top, this);
} else {
std::vector<Value*> RemoveVals;
RemoveVals.reserve(Remove.size());
// Create N1. for (SetVector<unsigned>::iterator I = Remove.begin(),
if (!n1) n1 = IG.newNode(V1); E = Remove.end(); I != E; ++I) {
Value *V = IG.node(*I)->getValue();
if (!V->use_empty())
RemoveVals.push_back(V);
}
VR.mergeInto(&RemoveVals[0], RemoveVals.size(), V1, Top, this);
}
}
// Migrate relationships from removed nodes to N1. if (mergeIGNode) {
Node *N1 = IG.node(n1); // Create N1.
for (SetVector<unsigned>::iterator I = Remove.begin(), E = Remove.end(); if (!n1) n1 = IG.newNode(V1);
I != E; ++I) {
unsigned n = *I; // Migrate relationships from removed nodes to N1.
Node *N = IG.node(n); Node *N1 = IG.node(n1);
for (Node::iterator NI = N->begin(), NE = N->end(); NI != NE; ++NI) { for (SetVector<unsigned>::iterator I = Remove.begin(), E = Remove.end();
if (NI->Subtree->DominatedBy(Top)) { I != E; ++I) {
if (NI->To == n1) { unsigned n = *I;
assert((NI->LV & EQ_BIT) && "Node inequal to itself."); Node *N = IG.node(n);
continue; for (Node::iterator NI = N->begin(), NE = N->end(); NI != NE; ++NI) {
if (NI->Subtree->DominatedBy(Top)) {
if (NI->To == n1) {
assert((NI->LV & EQ_BIT) && "Node inequal to itself.");
continue;
}
if (Remove.count(NI->To))
continue;
IG.node(NI->To)->update(n1, reversePredicate(NI->LV), Top);
N1->update(NI->To, NI->LV, Top);
} }
if (Remove.count(NI->To)) }
continue; }
IG.node(NI->To)->update(n1, reversePredicate(NI->LV), Top); // Point V2 (and all items in Remove) to N1.
N1->update(NI->To, NI->LV, Top); if (!n2)
IG.addEquality(n1, V2, Top);
else {
for (SetVector<unsigned>::iterator I = Remove.begin(),
E = Remove.end(); I != E; ++I) {
IG.addEquality(n1, IG.node(*I)->getValue(), Top);
}
}
// If !Remove.empty() then V2 = Remove[0]->getValue().
// Even when Remove is empty, we still want to process V2.
i = 0;
for (Value *R = V2; i == 0 || i < Remove.size(); ++i) {
if (i) R = IG.node(Remove[i])->getValue(); // skip n2.
if (Instruction *I2 = dyn_cast<Instruction>(R)) {
if (below(I2) ||
Top->DominatedBy(Forest->getNodeForBlock(I2->getParent())))
defToOps(I2);
}
for (Value::use_iterator UI = V2->use_begin(), UE = V2->use_end();
UI != UE;) {
Use &TheUse = UI.getUse();
++UI;
if (Instruction *I = dyn_cast<Instruction>(TheUse.getUser())) {
if (below(I) ||
Top->DominatedBy(Forest->getNodeForBlock(I->getParent())))
opsToDef(I);
}
} }
} }
} }
// Point V2 (and all items in Remove) to N1. // re-opsToDef all dominated users of V1.
if (!n2) if (Instruction *I = dyn_cast<Instruction>(V1)) {
IG.addEquality(n1, V2, Top); for (Value::use_iterator UI = I->use_begin(), UE = I->use_end();
else {
for (SetVector<unsigned>::iterator I = Remove.begin(),
E = Remove.end(); I != E; ++I) {
IG.addEquality(n1, IG.node(*I)->getValue(), Top);
}
}
// If !Remove.empty() then V2 = Remove[0]->getValue().
// Even when Remove is empty, we still want to process V2.
i = 0;
for (Value *R = V2; i == 0 || i < Remove.size(); ++i) {
if (i) R = IG.node(Remove[i])->getValue(); // skip n2.
if (Instruction *I2 = dyn_cast<Instruction>(R)) {
if (below(I2) ||
Top->DominatedBy(Forest->getNodeForBlock(I2->getParent())))
defToOps(I2);
}
for (Value::use_iterator UI = V2->use_begin(), UE = V2->use_end();
UI != UE;) { UI != UE;) {
Use &TheUse = UI.getUse(); Use &TheUse = UI.getUse();
++UI; ++UI;
if (Instruction *I = dyn_cast<Instruction>(TheUse.getUser())) { Value *V = TheUse.getUser();
if (below(I) || if (!V->use_empty()) {
Top->DominatedBy(Forest->getNodeForBlock(I->getParent()))) if (Instruction *Inst = dyn_cast<Instruction>(V)) {
opsToDef(I); if (below(Inst) ||
Top->DominatedBy(Forest->getNodeForBlock(Inst->getParent())))
opsToDef(Inst);
}
} }
} }
} }
@ -1471,26 +1537,47 @@ namespace {
return; return;
} }
// "%y = and i1 true, %x" then %x EQ %y. // "%y = and i1 true, %x" then %x EQ %y
// "%y = or i1 false, %x" then %x EQ %y. // "%y = or i1 false, %x" then %x EQ %y
if (BO->getOpcode() == Instruction::Or) { // "%x = add i32 %y, 0" then %x EQ %y
Constant *Zero = Constant::getNullValue(BO->getType()); // "%x = mul i32 %y, 0" then %x EQ 0
if (Op0 == Zero) {
add(BO, Op1, ICmpInst::ICMP_EQ, NewContext); Instruction::BinaryOps Opcode = BO->getOpcode();
return;
} else if (Op1 == Zero) { switch (Opcode) {
add(BO, Op0, ICmpInst::ICMP_EQ, NewContext); default: break;
return; case Instruction::Sub:
} case Instruction::Add:
} else if (BO->getOpcode() == Instruction::And) { case Instruction::Or: {
Constant *AllOnes = ConstantInt::getAllOnesValue(BO->getType()); Constant *Zero = Constant::getNullValue(BO->getType());
if (Op0 == AllOnes) { if (Op0 == Zero) {
add(BO, Op1, ICmpInst::ICMP_EQ, NewContext); add(BO, Op1, ICmpInst::ICMP_EQ, NewContext);
return; return;
} else if (Op1 == AllOnes) { } else if (Op1 == Zero) {
add(BO, Op0, ICmpInst::ICMP_EQ, NewContext); add(BO, Op0, ICmpInst::ICMP_EQ, NewContext);
return; return;
} }
} break;
case Instruction::And: {
Constant *AllOnes = ConstantInt::getAllOnesValue(BO->getType());
if (Op0 == AllOnes) {
add(BO, Op1, ICmpInst::ICMP_EQ, NewContext);
return;
} else if (Op1 == AllOnes) {
add(BO, Op0, ICmpInst::ICMP_EQ, NewContext);
return;
}
} break;
case Instruction::Mul: {
Constant *Zero = Constant::getNullValue(BO->getType());
if (Op0 == Zero) {
add(BO, Zero, ICmpInst::ICMP_EQ, NewContext);
return;
} else if (Op1 == Zero) {
add(BO, Zero, ICmpInst::ICMP_EQ, NewContext);
return;
}
} break;
} }
// "%x = add i32 %y, %z" and %x EQ %y then %z EQ 0 // "%x = add i32 %y, %z" and %x EQ %y then %z EQ 0
@ -1498,7 +1585,6 @@ namespace {
// 1. Repeat all of the above, with order of operands reversed. // 1. Repeat all of the above, with order of operands reversed.
// "%x = udiv i32 %y, %z" and %x EQ %y then %z EQ 1 // "%x = udiv i32 %y, %z" and %x EQ %y then %z EQ 1
Instruction::BinaryOps Opcode = BO->getOpcode();
const Type *Ty = BO->getType(); const Type *Ty = BO->getType();
assert(!Ty->isFPOrFPVector() && "Float in work queue!"); assert(!Ty->isFPOrFPVector() && "Float in work queue!");
@ -1704,6 +1790,12 @@ namespace {
VRP->add(V, ConstantInt::get(*I), Pred, VRP->TopInst); VRP->add(V, ConstantInt::get(*I), Pred, VRP->TopInst);
} }
#ifndef NDEBUG
bool ValueRanges::isCanonical(Value *V, ETNode *Subtree, VRPSolver *VRP) {
return V == VRP->IG.canonicalize(V, Subtree);
}
#endif
/// PredicateSimplifier - This class is a simplifier that replaces /// PredicateSimplifier - This class is a simplifier that replaces
/// one equivalent variable with another. It also tracks what /// one equivalent variable with another. It also tracks what
/// can't be equal and will solve setcc instructions when possible. /// can't be equal and will solve setcc instructions when possible.

View File

@ -0,0 +1,19 @@
; RUN: llvm-as < %s | opt -predsimplify | llvm-dis | grep -v %c
define void @foo(i8* %X, i8* %Y) {
entry:
%A = load i8* %X
%B = load i8* %Y
%a = icmp ult i8 %B, 10
br i1 %a, label %cond_true, label %URB
cond_true:
%b = icmp eq i8 %A, %B
br i1 %b, label %cond_true2, label %URB
cond_true2:
%c = icmp ult i8 %A, 11
call i8 @bar(i1 %c)
ret void
URB:
ret void
}
declare i8 @bar(i1)