GP-4790 Collapse double precision COPY to address forced storage

This commit is contained in:
caheckman 2024-07-22 21:12:51 +00:00
parent be305db930
commit 614f20cfa3
7 changed files with 360 additions and 46 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.
@ -1220,6 +1220,13 @@ int4 SplitVarnode::applyRuleIn(SplitVarnode &in,Funcdata &data)
return 1;
}
break;
case CPUI_COPY:
if (workop->getOut()->isAddrForce()) {
CopyForceForm copyform;
if (copyform.applyRule(in, workop, workishi, data))
return 1;
}
break;
default:
break;
}
@ -1385,6 +1392,47 @@ void SplitVarnode::replaceIndirectOp(Funcdata &data,SplitVarnode &out,SplitVarno
out.buildHiFromWhole(data);
}
/// \brief Rewrite the double precision version of a COPY to an address forced Varnode
///
/// This assumes that we have checked that the transformation is possible. The logical input must already
/// exist, and after this method is called, the logical output will also exist. The original COPY pieces
/// are explicitly destroyed.
/// \param data is the function owning the COPYs
/// \param addr is the storage address being COPYed
/// \param in is the input to the COPYs
/// \param copylo is the original least significant COPY
/// \param copyhi is the original most significant COPY
void SplitVarnode::replaceCopyForce(Funcdata &data,const Address &addr,SplitVarnode &in,PcodeOp *copylo,PcodeOp *copyhi)
{
Varnode *inVn = in.getWhole();
bool returnForm = copyhi->stopsCopyPropagation();
if (returnForm && inVn->getAddr() != addr) {
// Placeholder for global propagation past a RETURN needs an additional COPY
PcodeOp *otherPoint1 = copyhi->getIn(0)->getDef();
PcodeOp *otherPoint2 = copylo->getIn(0)->getDef();
// We know these are COPYs in the same basic block. Compute the later one.
if (otherPoint1->getSeqNum().getOrder() < otherPoint2->getSeqNum().getOrder())
otherPoint1 = otherPoint2;
PcodeOp *otherCopy = data.newOp(1, otherPoint1->getAddr());
data.opSetOpcode(otherCopy, CPUI_COPY);
Varnode *vn = data.newVarnodeOut(in.getSize(), addr, otherCopy);
data.opSetInput(otherCopy,inVn,0);
data.opInsertBefore(otherCopy, otherPoint1);
inVn = vn;
}
PcodeOp *wholeCopy = data.newOp(1, copyhi->getAddr());
data.opSetOpcode(wholeCopy, CPUI_COPY);
Varnode *outVn = data.newVarnodeOut(in.getSize(), addr, wholeCopy);
outVn->setAddrForce();
if (returnForm)
wholeCopy->setStopCopyPropagation();
data.opSetInput(wholeCopy,inVn,0);
data.opInsertBefore(wholeCopy, copyhi);
data.opDestroy(copyhi); // Destroy the original COPYs. Outputs have no descendants.
data.opDestroy(copylo);
}
bool AddForm::checkForCarry(PcodeOp *op)
{ // If -op- matches a CARRY construction based on lo1 (i.e. CARRY(x,lo1) )
@ -3151,6 +3199,73 @@ bool IndirectForm::applyRule(SplitVarnode &i,PcodeOp *ind,bool workishi,Funcdata
return true;
}
/// Starting with the input pieces, identify the matching COPYs and verify that they act as a single
/// address forced COPY with no descendants.
/// \param h is the most significant input piece
/// \param l is the least significant input piece
/// \param w is the preexisting logical whole
/// \param cpy is the COPY of the most significant piece
bool CopyForceForm::verify(Varnode *h,Varnode *l,Varnode *w,PcodeOp *cpy)
{
if (w == (Varnode *)0)
return false;
copyhi = cpy;
if (copyhi->getIn(0) != h) return false;
reshi = copyhi->getOut();
if (!reshi->isAddrForce() || !reshi->hasNoDescend())
return false;
list<PcodeOp *>::const_iterator iter,enditer;
iter = l->beginDescend();
enditer = l->endDescend();
while(iter != enditer) {
copylo = *iter;
++iter;
if (copylo->code() != CPUI_COPY || copylo->getParent() != copyhi->getParent())
continue;
reslo = copylo->getOut();
if (!reslo->isAddrForce() || !reslo->hasNoDescend())
continue;
if (!SplitVarnode::isAddrTiedContiguous(reslo, reshi, addrOut)) // Output MUST be contiguous addresses
continue;
if (copyhi->stopsCopyPropagation()) { // Special form has additional requirements
if (h->loneDescend() == (PcodeOp *)0)
continue;
if (l->loneDescend() == (PcodeOp *)0)
continue;
if (w->getAddr() != addrOut) { // Input whole MUST also be the same address
// Unless there are addition COPYs from the same basic block
if (!h->isWritten() || !l->isWritten())
continue;
PcodeOp *otherLo = l->getDef();
PcodeOp *otherHi = h->getDef();
if (otherLo->code() != CPUI_COPY || otherHi->code() != CPUI_COPY)
continue;
if (otherLo->getParent() != otherHi->getParent())
continue;
}
}
return true;
}
return false;
}
/// \param i is the putative input to the COPYs
/// \param cpy is a putative COPY
/// \param workishi is \b true if the COPY is of the most significant piece
/// \param data is the function
bool CopyForceForm::applyRule(SplitVarnode &i,PcodeOp *cpy,bool workishi,Funcdata &data)
{
if (!workishi) return false;
if (!i.hasBothPieces()) return false;
in = i;
if (!verify(in.getHi(),in.getLo(),in.getWhole(),cpy))
return false;
SplitVarnode::replaceCopyForce(data, addrOut, in, copylo, copyhi);
return true;
}
void RuleDoubleIn::reset(Funcdata &data)
{
@ -3168,10 +3283,10 @@ void RuleDoubleIn::getOpList(vector<uint4> &oplist) const
/// If the given Varnode looks like the most significant piece, there is another SUBPIECE that looks
/// like the least significant piece, and the whole is from an operation that produces a logical whole,
/// then mark the Varnode (and its companion) as double precision pieces and return 1.
/// \param data is the function owning the Varnode
/// \param vn is the given Varnode
/// \param subpieceOp is the SUBPIECE PcodeOp producing the Varnode
int4 RuleDoubleIn::attemptMarking(Funcdata &data,Varnode *vn,PcodeOp *subpieceOp)
/// \return 1 if the pieces are marked, 0 otherwise
int4 RuleDoubleIn::attemptMarking(Varnode *vn,PcodeOp *subpieceOp)
{
Varnode *whole = subpieceOp->getIn(0);
@ -3190,36 +3305,10 @@ int4 RuleDoubleIn::attemptMarking(Funcdata &data,Varnode *vn,PcodeOp *subpieceOp
}
else {
// Categorize opcodes as "producing a logical whole"
switch(whole->getDef()->code()) {
case CPUI_INT_ADD:
// Its hard to tell if the bit operators are really being used to act on the "logical whole"
// case CPUI_INT_AND:
// case CPUI_INT_OR:
// case CPUI_INT_XOR:
// case CPUI_INT_NEGATE:
case CPUI_INT_MULT:
case CPUI_INT_DIV:
case CPUI_INT_SDIV:
case CPUI_INT_REM:
case CPUI_INT_SREM:
case CPUI_INT_2COMP:
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_DIV:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_NEG:
case CPUI_FLOAT_ABS:
case CPUI_FLOAT_SQRT:
case CPUI_FLOAT_INT2FLOAT:
case CPUI_FLOAT_FLOAT2FLOAT:
case CPUI_FLOAT_TRUNC:
case CPUI_FLOAT_CEIL:
case CPUI_FLOAT_FLOOR:
case CPUI_FLOAT_ROUND:
break;
default:
return 0;
}
// Its hard to tell if a logical op is really being used to act on the "logical whole"
TypeOp *typeop = whole->getDef()->getOpcode();
if (!typeop->isArithmeticOp() && !typeop->isFloatingPointOp())
return 0;
}
Varnode *vnLo = (Varnode *)0;
list<PcodeOp *>::const_iterator iter;
@ -3240,11 +3329,11 @@ int4 RuleDoubleIn::attemptMarking(Funcdata &data,Varnode *vn,PcodeOp *subpieceOp
int4 RuleDoubleIn::applyOp(PcodeOp *op,Funcdata &data)
{ // Try to push double precision object "down" one level from input
{
Varnode *outvn = op->getOut();
if (!outvn->isPrecisLo()) {
if (outvn->isPrecisHi()) return 0;
return attemptMarking(data, outvn, op);
return attemptMarking(outvn, op);
}
if (data.hasUnreachableBlocks()) return 0;
@ -3260,6 +3349,82 @@ int4 RuleDoubleIn::applyOp(PcodeOp *op,Funcdata &data)
return 0;
}
void RuleDoubleOut::getOpList(vector<uint4> &oplist) const
{
oplist.push_back(CPUI_PIECE);
}
/// \brief Determine if the given inputs to a PIECE should marked as double precision pieces
///
/// If the concatenation of the pieces is used as a logical whole by other ops, the two pieces
/// are marked and 1 is returned.
/// \param vnhi is the most significant input to the PIECE
/// \param vnlo is the least significant input
/// \param pieceOp is the op reading the pieces
/// \return 1 if the pieces are marked, 0 otherwise
int4 RuleDoubleOut::attemptMarking(Varnode *vnhi,Varnode *vnlo,PcodeOp *pieceOp)
{
Varnode *whole = pieceOp->getOut();
if (whole->isTypeLock()) {
if (!whole->getType()->isPrimitiveWhole())
return 0; // Don't mark for double precision if not a primitive type
}
if (vnhi->getSize() != vnlo->getSize())
return 0;
SymbolEntry *entryhi = vnhi->getSymbolEntry();
SymbolEntry *entrylo = vnlo->getSymbolEntry();
if (entryhi != (SymbolEntry *)0 || entrylo != (SymbolEntry *)0) {
if (entryhi == (SymbolEntry *)0 || entrylo == (SymbolEntry *)0)
return 0; // One has a symbol, one doesn't
if (entryhi->getSymbol() != entrylo->getSymbol())
return 0; // Not from the same symbol
}
bool isWhole = false;
list<PcodeOp *>::const_iterator iter;
for(iter=whole->beginDescend();iter!=whole->endDescend();++iter) {
TypeOp *typeop = (*iter)->getOpcode();
// Categorize op as "reading a logical whole"
if (typeop->isArithmeticOp() || typeop->isFloatingPointOp()) {
isWhole = true;
break;
}
}
if (!isWhole)
return 0;
vnhi->setPrecisHi();
vnlo->setPrecisLo();
return 1;
}
int4 RuleDoubleOut::applyOp(PcodeOp *op,Funcdata &data)
{
Varnode *vnhi= op->getIn(0);
Varnode *vnlo = op->getIn(1);
// Currently this only implements collapsing input varnodes read by CPUI_PIECE
// So we put the test for this particular case early
if (!vnhi->isInput() || !vnlo->isInput())
return 0;
if (!vnhi->isPersist() || !vnlo->isPersist())
return 0;
if (!vnhi->isPrecisHi() || !vnlo->isPrecisLo()) {
return attemptMarking(vnhi,vnlo,op);
}
if (data.hasUnreachableBlocks()) return 0;
Address addr;
if (!SplitVarnode::isAddrTiedContiguous(vnlo, vnhi, addr))
return 0;
data.combineInputVarnodes(vnhi,vnlo);
return 1;
}
/// \brief Scan for conflicts between two LOADs or STOREs that would prevent them from being combined
///
/// The PcodeOps must be in the same basic block. Each PcodeOp that falls in between is examined