ActionCollectLanedAccess

This commit is contained in:
caheckman 2019-11-06 10:02:07 -05:00
parent e7c75b663d
commit dddcf4c715
5 changed files with 202 additions and 60 deletions

View file

@ -495,6 +495,127 @@ int4 ActionStackPtrFlow::apply(Funcdata &data)
return 0;
}
/// Mark every PcodeOp that is reachable from the given Varnode and create a LanedAccess record.
/// \param data is the function to trace through
/// \param base is the description of the laned register being traced from
/// \param vn is the Varnode to trace
/// \param pos is the significance position of the Varnode within the laned register
void ActionCollectLanedAccess::traceVarnode(Funcdata &data,const LanedRegister *base,Varnode *vn,int4 pos)
{
list<PcodeOp *>::const_iterator iter = vn->beginDescend();
int4 step = 0;
while(step < 2) {
PcodeOp *op;
if (step == 0) {
op = *iter;
++iter;
if (iter == vn->endDescend())
step = 1;
}
else {
step = 2;
if (!vn->isWritten()) continue;
op = vn->getDef();
}
if (op->isMark()) continue;
// Make sure op is associated with a lane access records describing its biggest Varnode
switch(op->code()) {
case CPUI_LOAD:
case CPUI_PIECE:
case CPUI_INT_ZEXT:
case CPUI_INT_SEXT:
if (vn != op->getOut()) continue; // Biggest Varnode is the output
break;
case CPUI_SUBPIECE:
if (vn != op->getIn(0)) continue; // Biggest Varnode is first input
break;
case CPUI_STORE:
if (vn != op->getIn(2)) continue;
break;
default:
break;
}
op->setMark();
data.opMarkLanedAccess(base, op, vn->getSize(), pos);
}
}
/// \brief Search for Varnodes that match the given laned vector register
///
/// All varnodes (bigger than 8 bytes) that are contained in the vector register are processed,
/// looking for a working lane scheme. Data-flow from these Varnodes is traces and LanedAccess
/// records are created for each PcodeOp flowed through.
/// \param data is the function being traced
/// \param lanedRegister is the given register and acceptable lane schemes
/// \param iter is an iterator to the first Varnode that matches the vector register
void ActionCollectLanedAccess::processLane(Funcdata &data,const LanedRegister &lanedRegister,
VarnodeLocSet::const_iterator iter)
{
int4 fullSize = lanedRegister.getStorage().size;
Address startAddress(lanedRegister.getStorage().getAddr());
Address lastAddress(startAddress + (fullSize-1));
VarnodeLocSet::const_iterator enditer = data.endLoc();
while(iter != enditer) {
Varnode *vn = *iter;
++iter;
if (lastAddress < vn->getAddr())
break;
if (vn->getSize() <= 8) // Varnode not big enough to be vector register
continue;
int4 diff = (int4)(vn->getOffset() - startAddress.getOffset());
if (diff + vn->getSize() > fullSize) // Must be contained by full register
continue;
if (vn->getSpace()->isBigEndian())
diff = fullSize - (diff + vn->getSize());
traceVarnode(data, &lanedRegister, vn, diff);
}
}
/// Give each PcodeOp in the laned access list a chance to propagate to new PcodeOps,
/// which will have new records added to the list.
/// \param data is the function being traced
void ActionCollectLanedAccess::propagate(Funcdata &data)
{
list<LanedAccess>::const_iterator iter = data.beginLaneAccess();
while(iter != data.endLaneAccess()) {
const LanedAccess lanedAccess( *iter );
PcodeOp *op = lanedAccess.getOp();
int4 sz = lanedAccess.getSize();
for(int4 i=0;i<op->numInput();++i) {
Varnode *vn = op->getIn(i);
if (vn->getSize() != sz) continue; // Size must match exactly
if (vn->isConstant() || vn->isAnnotation()) continue;
traceVarnode(data, lanedAccess.getBase(), vn, lanedAccess.getBytePos());
}
++iter;
}
}
int4 ActionCollectLanedAccess::apply(Funcdata &data)
{
if (data.getArch()->lanerecords.empty())
return 0;
const LanedRegister &lastRecord( data.getArch()->lanerecords.back() );
Address lastAddress( lastRecord.getStorage().getAddr() + lastRecord.getStorage().size - 1);
list<LanedRegister>::const_iterator iter;
for(iter=data.getArch()->lanerecords.begin();iter!=data.getArch()->lanerecords.end();++iter) {
const LanedRegister &lanedRegister( *iter );
VarnodeLocSet::const_iterator viter = data.beginLoc(lanedRegister.getStorage().getAddr());
if (viter == data.endLoc()) break;
Varnode *vn = *viter;
if (lastAddress < vn->getAddr()) break;
processLane(data,lanedRegister,viter);
}
propagate(data);
list<LanedAccess>::const_iterator aiter;
for(aiter=data.beginLaneAccess();aiter!=data.endLaneAccess();++aiter)
(*aiter).getOp()->clearMark();
return 0;
}
/// \brief Try to divide a single Varnode into lanes
///
/// Look for a CPUI_SUBPIECE op that takes the given Varnode as the input or a
@ -505,8 +626,10 @@ int4 ActionStackPtrFlow::apply(Funcdata &data)
/// \param data is the function being transformed
/// \param vn is the given single Varnode
/// \param lanedRegister is acceptable set of lane sizes for the Varnode
/// \param bytePos is the significance position of the Varnode within the laned register
/// \param allowDowncast is \b true if we allow lane systems with SUBPIECE terminators
bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,bool allowDowncast)
bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,int4 bytePos,
bool allowDowncast)
{
list<PcodeOp *>::const_iterator iter = vn->beginDescend();
@ -536,10 +659,6 @@ bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegi
if (checkedLanes.allowedLane(curSize)) continue;
checkedLanes.addSize(curSize); // Only check this scheme once
LaneDescription description(lanedRegister.getStorage().size,curSize); // Lane scheme dictated by curSize
int4 bytePos = (int4)(lanedRegister.getStorage().offset - vn->getOffset());
if (lanedRegister.getStorage().space->isBigEndian()) {
bytePos = lanedRegister.getStorage().size - (bytePos + vn->getSize()); // Convert to significance order
}
if (!description.subset(bytePos,vn->getSize())) // Try to restrict lane scheme to actual Varnode
continue;
LaneDivide laneDivide(&data,vn,description,allowDowncast);
@ -553,63 +672,34 @@ bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegi
return false;
}
/// \brief Search for and attempt to split Varnodes that match the given laned vector register
///
/// All varnodes (bigger than 8 bytes) that are contained in the vector register are processed,
/// looking for a working lane scheme, and splitting based on that scheme. Return \b false if there
/// is at least one eligible Varnode that could not be split.
/// \param data is the function being modified
/// \param lanedRegister is the given register and acceptable lane schemes
/// \param allowDowncast is \b true if SUBPIECE terminators are allowed in the schemes
/// \param iter is an iterator to the first Varnode that matches the vector register
/// \return \b true if all varnodes were successfully split
bool ActionLaneDivide::processLane(Funcdata &data,const LanedRegister &lanedRegister,bool allowDowncast,
VarnodeLocSet::const_iterator iter)
{
int4 fullSize = lanedRegister.getStorage().size;
Address startAddress(lanedRegister.getStorage().getAddr());
Address lastAddress(startAddress + (fullSize-1));
VarnodeLocSet::const_iterator enditer = data.endLoc();
bool res = true;
while(iter != enditer) {
Varnode *vn = *iter;
++iter;
if (lastAddress < vn->getAddr())
break;
if (vn->getSize() <= 8) // Varnode not big enough to be vector register
continue;
int4 diff = (int4)(vn->getOffset() - startAddress.getOffset());
if (diff + vn->getSize() > fullSize) // Must be contained by full register
continue;
if (processVarnode(data,vn,lanedRegister,allowDowncast)) {
// If changes were made, iterator may no longer be valid, generate a new one
iter = data.beginLoc(startAddress);
res = true; // We may have eliminated a previous failure
}
else
res = false; // Note the failure
}
return res;
}
int4 ActionLaneDivide::apply(Funcdata &data)
{
if (data.getArch()->lanerecords.empty())
return 0;
const LanedRegister &lastRecord( data.getArch()->lanerecords.back() );
Address lastAddress( lastRecord.getStorage().getAddr() + lastRecord.getStorage().size - 1);
list<LanedRegister>::const_iterator iter;
for(iter=data.getArch()->lanerecords.begin();iter!=data.getArch()->lanerecords.end();++iter) {
const LanedRegister &lanedRegister( *iter );
VarnodeLocSet::const_iterator viter = data.beginLoc(lanedRegister.getStorage().getAddr());
if (viter == data.endLoc()) break;
Varnode *vn = *viter;
if (lastAddress < vn->getAddr()) break;
if (!processLane(data,lanedRegister,false,viter)) // Try without SUBPIECE terminators
processLane(data,lanedRegister,true,viter); // If we fail, try with SUBPIECE terminators
list<LanedAccess>::const_iterator iter;
bool allowDowncast = false;
for(int4 i=0;i<2;++i) {
for(iter=data.beginLaneAccess();iter!=data.endLaneAccess();++iter) {
const LanedAccess &lanedAccess(*iter);
PcodeOp *op = lanedAccess.getOp();
if (op->isDead()) continue;
Varnode *vn = op->getOut();
if (vn != (Varnode *)0 && vn->getSize() == lanedAccess.getSize())
processVarnode(data, vn, *lanedAccess.getBase(), lanedAccess.getBytePos(), allowDowncast);
else {
for(int4 j=0;j<op->numInput();++j) {
vn = op->getIn(j);
if (vn->getSize() == lanedAccess.getSize()) {
if (!vn->isConstant() && !vn->isAnnotation() && !vn->isWritten()) {
processVarnode(data, vn, *lanedAccess.getBase(), lanedAccess.getBytePos(), allowDowncast);
break;
}
}
}
}
}
allowDowncast = true;
}
data.clearLanedAccessList();
return 0;
}
@ -4624,6 +4714,7 @@ void universal_action(Architecture *conf)
// actmainloop->addAction( new ActionParamShiftStop("paramshift") );
actmainloop->addAction( new ActionRestrictLocal("localrecovery") ); // Do before dead code removed
actmainloop->addAction( new ActionDeadCode("deadcode") );
actmainloop->addAction( new ActionCollectLanedAccess("base") );
actmainloop->addAction( new ActionDynamicMapping("dynamic") ); // Must come before restructurevarnode and infertypes
actmainloop->addAction( new ActionRestructureVarnode("localrecovery") );
actmainloop->addAction( new ActionSpacebase("base") ); // Must come before infertypes and nonzeromask

View file

@ -97,6 +97,26 @@ public:
virtual int4 apply(Funcdata &data);
};
/// \brief Find PcodeOps that are accessing values from laned registers
///
/// This looks for Varnodes that match any of the LanedRegister records associated with
/// the architecture. A record is created in the function's LanedAccess list for every
/// PcodeOp that accesses one of these Varnodes. These are later examined by ActionLaneDivide.
/// The LanedRegister is the key for finding the Varnode, but the \e big property can propagate
/// through data-flow to other Varnodes that are not necessarily using the same storage (i.e. uniques).
class ActionCollectLanedAccess : public Action {
void traceVarnode(Funcdata &data,const LanedRegister *base,Varnode *vn,int4 pos); ///< Trace a big Varnode instance to other big Varnodes
void processLane(Funcdata &data,const LanedRegister &lanedRegister,VarnodeLocSet::const_iterator iter);
void propagate(Funcdata &data); ///< Discover other PcodeOps that use laned values
public:
ActionCollectLanedAccess(const string &g) : Action(rule_onceperfunc,"collectlanedaccess",g) {} ///< Constructor
virtual Action *clone(const ActionGroupList &grouplist) const {
if (!grouplist.contains(getGroup())) return (Action *)0;
return new ActionCollectLanedAccess(getGroup());
}
virtual int4 apply(Funcdata &data);
};
/// \brief Find Varnodes with a vectorized lane scheme and attempt to split the lanes
///
/// The Architecture lists (vector) registers that may be used to perform parallelized operations
@ -104,8 +124,7 @@ public:
/// if a particular lane scheme makes sense in terms of the function's data-flow, and then
/// rewrites the data-flow so that the lanes become explicit Varnodes.
class ActionLaneDivide : public Action {
bool processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,bool allowDowncast);
bool processLane(Funcdata &data,const LanedRegister &lanedRegister,bool allowDowncast,VarnodeLocSet::const_iterator iter);
bool processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister,int4 bytePos,bool allowDowncast);
public:
ActionLaneDivide(const string &g) : Action(rule_onceperfunc,"lanedivide",g) {} ///< Constructor
virtual Action *clone(const ActionGroupList &grouplist) const {

View file

@ -80,6 +80,7 @@ class Funcdata {
Merge covermerge; ///< Variable range intersection algorithms
ParamActive *activeoutput; ///< Data for assessing which parameters are passed to \b this function
Override localoverride; ///< Overrides of data-flow, prototypes, etc. that are local to \b this function
list<LanedAccess> lanedList; ///< List of ops that are accessing potentially laned registers
// Low level Varnode functions
void setVarnodeProperties(Varnode *vn) const; ///< Look-up boolean properties and data-type information
@ -428,6 +429,10 @@ public:
Varnode *opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Varnode *stackptr,bool insertafter);
PcodeOp *opStackStore(AddrSpace *spc,uintb off,PcodeOp *op,bool insertafter);
void opUndoPtradd(PcodeOp *op,bool finalize); ///< Convert a CPUI_PTRADD back into a CPUI_INT_ADD
void opMarkLanedAccess(const LanedRegister *base,PcodeOp *op,int4 sz,int4 pos); ///< Mark op as using laned register
list<LanedAccess>::const_iterator beginLaneAccess(void) const { return lanedList.begin(); } ///< Beginning iterator over laned accesses
list<LanedAccess>::const_iterator endLaneAccess(void) const { return lanedList.end(); } ///< Ending iterator over laned accesses
void clearLanedAccessList(void) { lanedList.clear(); } ///< Clear records from the laned access list
/// \brief Start of PcodeOp objects with the given op-code
list<PcodeOp *>::const_iterator beginOp(OpCode opc) const { return obank.begin(opc); }

View file

@ -557,6 +557,19 @@ void Funcdata::opUndoPtradd(PcodeOp *op,bool finalize)
opInsertBefore(multOp,op);
}
/// Store a record indicating a potential use of a large laned register. Generally, the output
/// of the op is the use, except in the case of SUBPIECE or STORE, where getIn(0) and getIn(2)
/// respectively are the Varnodes being used.
/// \param base is the description of the laned register that is triggering this record
/// \param op is the PcodeOp using the large Varnode
/// \param sz is the size of the large Varnode in bytes
/// \param pos is the significance position of the Varnode, relative to the LanedRegister description
void Funcdata::opMarkLanedAccess(const LanedRegister *base,PcodeOp *op,int4 sz,int4 pos)
{
lanedList.push_back(LanedAccess(base,op,sz,pos));
}
/// Make a clone of the given PcodeOp, copying control-flow properties as well. The data-type
/// is \e not cloned.
/// \param op is the PcodeOp to clone

View file

@ -98,6 +98,20 @@ public:
bool operator<(const LanedRegister &op2) const { return (storage < op2.storage); } ///< Compare based on VarnodeData
};
/// \brief Record describing the access of a large Varnode that can be traced to a LanedRegister
class LanedAccess {
const LanedRegister *base; ///< Base register dictating the lane scheme
PcodeOp *op; ///< Operation using the big register
int4 size; ///< Size of the register in bytes
int4 bytePos; ///< Significance position relative to the laned register
public:
LanedAccess(const LanedRegister *b,PcodeOp *o,int4 sz,int4 pos) { base=b; op=o; size=sz; bytePos=pos; } ///< Constructor
const LanedRegister *getBase(void) const { return base; } ///< Get the base LanedRegister being traced
PcodeOp *getOp(void) const { return op; } ///< Get the PcodeOp using the large Varnode
int4 getSize(void) const { return size; } ///< Get the size of the Varnode being accessed
int4 getBytePos(void) const { return bytePos; } ///< Get the significance position relative to the laned register
};
/// \brief Description of logical lanes within a \b big Varnode
///
/// A \b lane is a byte offset and size within a Varnode. Lanes within a