GP-4979 Better support for partial array optimizations

This commit is contained in:
caheckman 2024-10-28 19:34:30 +00:00
parent 43655ab76c
commit 784540f1c0
14 changed files with 317 additions and 134 deletions

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -1100,6 +1100,66 @@ bool Funcdata::collapseIntMultMult(Varnode *vn)
return true;
}
/// Return a Varnode in the \e unique space that is defined by a COPY op taking the given Varnode as input.
/// If a COPY op to a \e unique already exists, it may be returned. If the preexisting COPY is not usable
/// at the specified \b point, it is redefined at an earlier point in the control-flow so that it can be used.
/// \param vn is the given Varnode to COPY
/// \param point is the PcodeOp where the copy needs to be available
/// \return the \e unique Varnode COPY
Varnode *Funcdata::buildCopyTemp(Varnode *vn,PcodeOp *point)
{
PcodeOp *otherOp = (PcodeOp *)0;
PcodeOp *usedCopy = (PcodeOp *)0;
list<PcodeOp *>::const_iterator iter;
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter;
if (op->code() != CPUI_COPY) continue;
Varnode *outvn = op->getOut();
if (outvn->getSpace()->getType() == IPTR_INTERNAL) {
if (outvn->isTypeLock())
continue;
otherOp = op;
break;
}
}
if (otherOp != (PcodeOp *)0) {
if (point->getParent() == otherOp->getParent()) {
if (point->getSeqNum().getOrder() < otherOp->getSeqNum().getOrder())
usedCopy = (PcodeOp *)0;
else
usedCopy = otherOp;
}
else {
BlockBasic *common;
common = (BlockBasic *)FlowBlock::findCommonBlock(point->getParent(),otherOp->getParent());
if (common == point->getParent())
usedCopy = (PcodeOp *)0;
else if (common == otherOp->getParent())
usedCopy = otherOp;
else { // Neither op is ancestor of the other
usedCopy = newOp(1,common->getStop());
opSetOpcode(usedCopy,CPUI_COPY);
newUniqueOut(vn->getSize(),usedCopy);
opSetInput(usedCopy,vn,0);
opInsertEnd(usedCopy,common);
}
}
}
if (usedCopy == (PcodeOp *)0) {
usedCopy = newOp(1,point->getAddr());
opSetOpcode(usedCopy, CPUI_COPY);
newUniqueOut(vn->getSize(), usedCopy);
opSetInput(usedCopy, vn, 0);
opInsertBefore(usedCopy, point);
}
if (otherOp != (PcodeOp *)0 && otherOp != usedCopy) {
totalReplace(otherOp->getOut(),usedCopy->getOut());
opDestroy(otherOp);
}
return usedCopy->getOut();
}
/// \brief Trace a boolean value to a set of PcodeOps that can be changed to flip the boolean value
///
/// The boolean Varnode is either the output of the given PcodeOp or the
@ -1108,7 +1168,7 @@ bool Funcdata::collapseIntMultMult(Varnode *vn)
/// \param op is the given PcodeOp
/// \param fliplist is the array that will hold the ops to flip
/// \return 0 if the change normalizes, 1 if the change is ambivalent, 2 if the change does not normalize
int4 opFlipInPlaceTest(PcodeOp *op,vector<PcodeOp *> &fliplist)
int4 Funcdata::opFlipInPlaceTest(PcodeOp *op,vector<PcodeOp *> &fliplist)
{
Varnode *vn;
@ -1168,7 +1228,7 @@ int4 opFlipInPlaceTest(PcodeOp *op,vector<PcodeOp *> &fliplist)
/// facilitate the flip.
/// \param data is the function being modified
/// \param fliplist is the list of PcodeOps to modify
void opFlipInPlaceExecute(Funcdata &data,vector<PcodeOp *> &fliplist)
void Funcdata::opFlipInPlaceExecute(vector<PcodeOp *> &fliplist)
{
Varnode *vn;
@ -1180,53 +1240,29 @@ void opFlipInPlaceExecute(Funcdata &data,vector<PcodeOp *> &fliplist)
vn = op->getIn(0);
PcodeOp *otherop = op->getOut()->loneDescend(); // Must be a lone descendant
int4 slot = otherop->getSlot(op->getOut());
data.opSetInput(otherop,vn,slot); // Propagate -vn- into otherop
data.opDestroy(op);
opSetInput(otherop,vn,slot); // Propagate -vn- into otherop
opDestroy(op);
}
else if (opc == CPUI_MAX) {
if (op->code() == CPUI_BOOL_AND)
data.opSetOpcode(op,CPUI_BOOL_OR);
opSetOpcode(op,CPUI_BOOL_OR);
else if (op->code() == CPUI_BOOL_OR)
data.opSetOpcode(op,CPUI_BOOL_AND);
opSetOpcode(op,CPUI_BOOL_AND);
else
throw LowlevelError("Bad flipInPlace op");
}
else {
data.opSetOpcode(op,opc);
opSetOpcode(op,opc);
if (flipyes) {
data.opSwapInput(op,0,1);
opSwapInput(op,0,1);
if ((opc == CPUI_INT_LESSEQUAL)||(opc == CPUI_INT_SLESSEQUAL))
data.replaceLessequal(op);
replaceLessequal(op);
}
}
}
}
/// \brief Get the earliest use/read of a Varnode in a specified basic block
///
/// \param vn is the Varnode to search for
/// \param bl is the specified basic block in which to search
/// \return the earliest PcodeOp reading the Varnode or NULL
PcodeOp *earliestUseInBlock(Varnode *vn,BlockBasic *bl)
{
list<PcodeOp *>::const_iterator iter;
PcodeOp *res = (PcodeOp *)0;
for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
PcodeOp *op = *iter;
if (op->getParent() != bl) continue;
if (res == (PcodeOp *)0)
res = op;
else {
if (op->getSeqNum().getOrder() < res->getSeqNum().getOrder())
res = op;
}
}
return res;
}
/// \brief Find a duplicate calculation of a given PcodeOp reading a specific Varnode
///
/// We only match 1 level of calculation. Additionally the duplicate must occur in the
@ -1236,7 +1272,7 @@ PcodeOp *earliestUseInBlock(Varnode *vn,BlockBasic *bl)
/// \param bl is the indicated basic block
/// \param earliest is the specified op to be earlier than
/// \return the discovered duplicate PcodeOp or NULL
PcodeOp *cseFindInBlock(PcodeOp *op,Varnode *vn,BlockBasic *bl,PcodeOp *earliest)
PcodeOp *Funcdata::cseFindInBlock(PcodeOp *op,Varnode *vn,BlockBasic *bl,PcodeOp *earliest)
{
list<PcodeOp *>::const_iterator iter;
@ -1265,11 +1301,10 @@ PcodeOp *cseFindInBlock(PcodeOp *op,Varnode *vn,BlockBasic *bl,PcodeOp *earliest
/// (depth 1 functional equivalence) eliminate the redundancy. Return the remaining (dominating)
/// PcodeOp. If neither op dominates the other, both are eliminated, and a new PcodeOp
/// is built at a commonly accessible point.
/// \param data is the function being modified
/// \param op1 is the first of the given PcodeOps
/// \param op2 is the second given PcodeOp
/// \return the dominating PcodeOp
PcodeOp *cseElimination(Funcdata &data,PcodeOp *op1,PcodeOp *op2)
PcodeOp *Funcdata::cseElimination(PcodeOp *op1,PcodeOp *op2)
{
PcodeOp *replace;
@ -1288,25 +1323,25 @@ PcodeOp *cseElimination(Funcdata &data,PcodeOp *op1,PcodeOp *op2)
else if (common == op2->getParent())
replace = op2;
else { // Neither op is ancestor of the other
replace = data.newOp(op1->numInput(),common->getStop());
data.opSetOpcode(replace,op1->code());
data.newVarnodeOut(op1->getOut()->getSize(),op1->getOut()->getAddr(),replace);
replace = newOp(op1->numInput(),common->getStop());
opSetOpcode(replace,op1->code());
newVarnodeOut(op1->getOut()->getSize(),op1->getOut()->getAddr(),replace);
for(int4 i=0;i<op1->numInput();++i) {
if (op1->getIn(i)->isConstant())
data.opSetInput(replace,data.newConstant(op1->getIn(i)->getSize(),op1->getIn(i)->getOffset()),i);
opSetInput(replace,newConstant(op1->getIn(i)->getSize(),op1->getIn(i)->getOffset()),i);
else
data.opSetInput(replace,op1->getIn(i),i);
opSetInput(replace,op1->getIn(i),i);
}
data.opInsertEnd(replace,common);
opInsertEnd(replace,common);
}
}
if (replace != op1) {
data.totalReplace(op1->getOut(),replace->getOut());
data.opDestroy(op1);
totalReplace(op1->getOut(),replace->getOut());
opDestroy(op1);
}
if (replace != op2) {
data.totalReplace(op2->getOut(),replace->getOut());
data.opDestroy(op2);
totalReplace(op2->getOut(),replace->getOut());
opDestroy(op2);
}
return replace;
}
@ -1329,10 +1364,9 @@ static bool compareCseHash(const pair<uintm,PcodeOp *> &a,const pair<uintm,Pcode
/// The hash serves as a primary test for duplicate calculations; if it doesn't match
/// the PcodeOps aren't common subexpressions. This method searches for hash matches
/// then does secondary testing and eliminates any redundancy it finds.
/// \param data is the function being modified
/// \param list is the list of (hash, PcodeOp) pairs
/// \param outlist will hold Varnodes produced by duplicate calculations
void cseEliminateList(Funcdata &data,vector< pair<uintm,PcodeOp *> > &list,vector<Varnode *> &outlist)
void Funcdata::cseEliminateList(vector< pair<uintm,PcodeOp *> > &list,vector<Varnode *> &outlist)
{
PcodeOp *op1,*op2,*resop;
@ -1350,9 +1384,9 @@ void cseEliminateList(Funcdata &data,vector< pair<uintm,PcodeOp *> > &list,vecto
if ((!op1->isDead())&&(!op2->isDead())&&op1->isCseMatch(op2)) {
Varnode *outvn1 = op1->getOut();
Varnode *outvn2 = op2->getOut();
if ((outvn1 == (Varnode *)0)||data.isHeritaged(outvn1)) {
if ((outvn2 == (Varnode *)0)||data.isHeritaged(outvn2)) {
resop = cseElimination(data,op1,op2);
if ((outvn1 == (Varnode *)0)||isHeritaged(outvn1)) {
if ((outvn2 == (Varnode *)0)||isHeritaged(outvn2)) {
resop = cseElimination(op1,op2);
outlist.push_back(resop->getOut());
}
}