mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 02:09:44 +02:00
Merge remote-tracking branch 'origin/GP-5581_sleighPiecewiseUniques--SQUASHED'
This commit is contained in:
commit
6773801f6e
20 changed files with 798 additions and 482 deletions
|
@ -583,9 +583,6 @@ VarnodeTpl *PcodeCompile::buildTruncatedVarnode(VarnodeTpl *basevn,uint4 bitoffs
|
|||
if ((bitoffset % 8) != 0) return (VarnodeTpl *)0;
|
||||
if ((numbits % 8) != 0) return (VarnodeTpl *)0;
|
||||
|
||||
if (basevn->getSpace().isUniqueSpace()) // Do we really want to prevent truncated uniques??
|
||||
return (VarnodeTpl *)0;
|
||||
|
||||
ConstTpl::const_type offset_type = basevn->getOffset().getType();
|
||||
if ((offset_type != ConstTpl::real)&&(offset_type != ConstTpl::handle))
|
||||
return (VarnodeTpl *)0;
|
||||
|
|
|
@ -540,6 +540,18 @@ void VarnodeTpl::decode(Decoder &decoder)
|
|||
decoder.closeElement(el);
|
||||
}
|
||||
|
||||
bool VarnodeTpl::operator==(const VarnodeTpl &op2) const
|
||||
|
||||
{
|
||||
return space==op2.space && offset==op2.offset && size==op2.size;
|
||||
}
|
||||
|
||||
bool VarnodeTpl::operator!=(const VarnodeTpl &op2) const
|
||||
|
||||
{
|
||||
return !(*this == op2);
|
||||
}
|
||||
|
||||
bool VarnodeTpl::operator<(const VarnodeTpl &op2) const
|
||||
|
||||
{
|
||||
|
|
|
@ -91,6 +91,8 @@ public:
|
|||
bool isDynamic(const ParserWalker &walker) const;
|
||||
int4 transfer(const vector<HandleTpl *> ¶ms);
|
||||
bool isZeroSize(void) const { return size.isZero(); }
|
||||
bool operator==(const VarnodeTpl &op2) const;
|
||||
bool operator!=(const VarnodeTpl &op2) const;
|
||||
bool operator<(const VarnodeTpl &op2) const;
|
||||
void setOffset(uintb constVal) { offset = ConstTpl(ConstTpl::real,constVal); }
|
||||
void setRelative(uintb constVal) { offset = ConstTpl(ConstTpl::j_relative,constVal); }
|
||||
|
|
|
@ -180,6 +180,75 @@ SubtableSymbol *WithBlock::getCurrentSubtable(const list<WithBlock> &stack)
|
|||
return (SubtableSymbol *)0;
|
||||
}
|
||||
|
||||
void ConsistencyChecker::OptimizeRecord::copyFromExcludingSize(ConsistencyChecker::OptimizeRecord &that)
|
||||
|
||||
{
|
||||
this->writeop = that.writeop;
|
||||
this->readop = that.readop;
|
||||
this->inslot = that.inslot;
|
||||
this->writecount = that.writecount;
|
||||
this->readcount = that.readcount;
|
||||
this->writesection = that.writesection;
|
||||
this->readsection = that.readsection;
|
||||
this->opttype = that.opttype;
|
||||
}
|
||||
|
||||
void ConsistencyChecker::OptimizeRecord::update(int4 opIdx, int4 slotIdx, int4 secNum)
|
||||
|
||||
{
|
||||
if (slotIdx >= 0) {
|
||||
updateRead(opIdx, slotIdx, secNum);
|
||||
}
|
||||
else {
|
||||
updateWrite(opIdx, secNum);
|
||||
}
|
||||
}
|
||||
|
||||
void ConsistencyChecker::OptimizeRecord::updateRead(int4 i, int4 inslot, int4 secNum)
|
||||
|
||||
{
|
||||
this->readop = i;
|
||||
this->readcount++;
|
||||
this->inslot = inslot;
|
||||
this->readsection = secNum;
|
||||
}
|
||||
|
||||
void ConsistencyChecker::OptimizeRecord::updateWrite(int4 i, int4 secNum)
|
||||
|
||||
{
|
||||
this->writeop = i;
|
||||
this->writecount++;
|
||||
this->writesection = secNum;
|
||||
}
|
||||
|
||||
void ConsistencyChecker::OptimizeRecord::updateExport()
|
||||
|
||||
{
|
||||
this->writeop = 0;
|
||||
this->readop = 0;
|
||||
this->writecount = 2;
|
||||
this->readcount = 2;
|
||||
this->readsection = -2;
|
||||
this->writesection = -2;
|
||||
}
|
||||
|
||||
void ConsistencyChecker::OptimizeRecord::updateCombine(ConsistencyChecker::OptimizeRecord &that)
|
||||
|
||||
{
|
||||
if (that.writecount != 0) {
|
||||
this->writeop = that.writeop;
|
||||
this->writesection = that.writesection;
|
||||
}
|
||||
if (that.readcount != 0) {
|
||||
this->readop = that.readop;
|
||||
this->inslot = that.inslot;
|
||||
this->readsection = that.readsection;
|
||||
}
|
||||
this->writecount += that.writecount;
|
||||
this->readcount += that.readcount;
|
||||
// opttype is not relevant here
|
||||
}
|
||||
|
||||
/// \brief Construct the consistency checker and optimizer
|
||||
///
|
||||
/// \param sleigh is the parsed SLEIGH spec
|
||||
|
@ -1129,6 +1198,90 @@ void ConsistencyChecker::setPostOrder(SubtableSymbol *root)
|
|||
}
|
||||
}
|
||||
|
||||
map<uintb,ConsistencyChecker::OptimizeRecord>::iterator ConsistencyChecker::UniqueState::lesserIter(uintb offset)
|
||||
|
||||
{
|
||||
if (recs.begin() == recs.end()) {
|
||||
return recs.end();
|
||||
}
|
||||
map<uintb,OptimizeRecord>::iterator iter;
|
||||
iter = recs.lower_bound(offset);
|
||||
if (iter == recs.begin()) {
|
||||
return recs.end();
|
||||
}
|
||||
return std::prev(iter);
|
||||
}
|
||||
|
||||
ConsistencyChecker::OptimizeRecord ConsistencyChecker::UniqueState::coalesce(vector<ConsistencyChecker::OptimizeRecord*> &records)
|
||||
|
||||
{
|
||||
uintb minOff = -1;
|
||||
uintb maxOff = -1;
|
||||
vector<OptimizeRecord*>::iterator iter;
|
||||
|
||||
for (iter = records.begin(); iter != records.end(); ++iter) {
|
||||
if (minOff == -1 || (*iter)->offset < minOff) {
|
||||
minOff = (*iter)->offset;
|
||||
}
|
||||
if (maxOff == -1 || (*iter)->offset + (*iter)->size > maxOff) {
|
||||
maxOff = (*iter)->offset + (*iter)->size;
|
||||
}
|
||||
}
|
||||
|
||||
OptimizeRecord result(minOff, maxOff - minOff);
|
||||
|
||||
for (iter = records.begin(); iter != records.end(); ++iter) {
|
||||
result.updateCombine(**iter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ConsistencyChecker::UniqueState::set(uintb offset, int4 size, OptimizeRecord &rec)
|
||||
|
||||
{
|
||||
vector<OptimizeRecord*> records;
|
||||
getDefinitions(records, offset, size);
|
||||
records.push_back(&rec);
|
||||
OptimizeRecord coalesced = coalesce(records);
|
||||
recs.erase(recs.lower_bound(coalesced.offset), recs.lower_bound(coalesced.offset+coalesced.size));
|
||||
recs.insert(pair<uint4,OptimizeRecord>(coalesced.offset, coalesced));
|
||||
}
|
||||
|
||||
void ConsistencyChecker::UniqueState::getDefinitions(vector<ConsistencyChecker::OptimizeRecord*> &result, uintb offset, int4 size)
|
||||
|
||||
{
|
||||
if (size == 0) {
|
||||
size = 1;
|
||||
}
|
||||
map<uintb,OptimizeRecord>::iterator iter;
|
||||
iter = lesserIter(offset);
|
||||
uintb cursor = offset;
|
||||
if (iter != recs.end() && endOf(iter) > offset) {
|
||||
OptimizeRecord &preRec = iter->second;
|
||||
cursor = endOf(iter);
|
||||
result.push_back(&preRec);
|
||||
}
|
||||
uintb end = offset + size;
|
||||
iter = recs.lower_bound(offset);
|
||||
while (iter != recs.end() && iter->first < end) {
|
||||
if (iter->first > cursor) {
|
||||
// The iterator becomes invalid with this insertion, so take the new one.
|
||||
iter = recs.insert(pair<uint4,OptimizeRecord>(cursor,OptimizeRecord(cursor, iter->first - cursor))).first;
|
||||
result.push_back(&iter->second);
|
||||
iter++; // Put the (now valid) iterator back to where it was.
|
||||
}
|
||||
// No need to truncate, as we're just counting a read
|
||||
result.push_back(&iter->second);
|
||||
cursor = endOf(iter);
|
||||
iter++;
|
||||
}
|
||||
if (end > cursor) {
|
||||
iter = recs.insert(pair<uint4,OptimizeRecord>(cursor,OptimizeRecord(cursor, end - cursor))).first;
|
||||
result.push_back(&iter->second);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Test whether two given Varnodes intersect
|
||||
///
|
||||
/// This test must be conservative. If it can't explicitly prove that the
|
||||
|
@ -1222,30 +1375,31 @@ bool ConsistencyChecker::readWriteInterference(const VarnodeTpl *vn,const OpTpl
|
|||
/// If the Varnode is in the \e unique space, an OptimizationRecord for it is looked
|
||||
/// up based on its offset. Information about how a p-code operator uses the Varnode
|
||||
/// is accumulated in the record.
|
||||
/// \param recs is collection of OptimizationRecords associated with temporary Varnodes
|
||||
/// \param state is collection of OptimizationRecords associated with temporary Varnodes
|
||||
/// \param vn is the given Varnode to check (which may or may not be temporary)
|
||||
/// \param i is the index of the operator using the Varnode (within its p-code section)
|
||||
/// \param inslot is the \e slot index of the Varnode within its operator
|
||||
/// \param secnum is the section number containing the operator
|
||||
void ConsistencyChecker::examineVn(map<uintb,OptimizeRecord> &recs,
|
||||
void ConsistencyChecker::examineVn(UniqueState &state,
|
||||
const VarnodeTpl *vn,uint4 i,int4 inslot,int4 secnum)
|
||||
{
|
||||
if (vn == (const VarnodeTpl *)0) return;
|
||||
if (!vn->getSpace().isUniqueSpace()) return;
|
||||
if (vn->getOffset().getType() != ConstTpl::real) return;
|
||||
|
||||
map<uintb,OptimizeRecord>::iterator iter;
|
||||
iter = recs.insert( pair<uint4,OptimizeRecord>(vn->getOffset().getReal(),OptimizeRecord())).first;
|
||||
uintb offset = vn->getOffset().getReal();
|
||||
int4 size = vn->getSize().getReal();
|
||||
if (inslot >= 0) {
|
||||
(*iter).second.readop = i;
|
||||
(*iter).second.readcount += 1;
|
||||
(*iter).second.inslot = inslot;
|
||||
(*iter).second.readsection = secnum;
|
||||
vector<OptimizeRecord*> defs;
|
||||
state.getDefinitions(defs,offset,size);
|
||||
for (vector<OptimizeRecord*>::iterator iter=defs.begin();iter!=defs.end();++iter) {
|
||||
(*iter)->updateRead(i,inslot,secnum);
|
||||
}
|
||||
}
|
||||
else {
|
||||
(*iter).second.writeop = i;
|
||||
(*iter).second.writecount += 1;
|
||||
(*iter).second.writesection = secnum;
|
||||
OptimizeRecord rec(offset,size);
|
||||
rec.updateWrite(i,secnum);
|
||||
state.set(offset,size,rec);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1254,9 +1408,9 @@ void ConsistencyChecker::examineVn(map<uintb,OptimizeRecord> &recs,
|
|||
/// For each temporary Varnode, count how many times it is read from or written to
|
||||
/// in the given section of p-code operators.
|
||||
/// \param ct is the given Constructor
|
||||
/// \param recs is the (initially empty) collection of count records
|
||||
/// \param state is the (initially empty) collection of count records
|
||||
/// \param secnum is the given p-code section number
|
||||
void ConsistencyChecker::optimizeGather1(Constructor *ct,map<uintb,OptimizeRecord> &recs,int4 secnum) const
|
||||
void ConsistencyChecker::optimizeGather1(Constructor *ct,UniqueState &state,int4 secnum) const
|
||||
|
||||
{
|
||||
ConstructTpl *tpl;
|
||||
|
@ -1271,10 +1425,10 @@ void ConsistencyChecker::optimizeGather1(Constructor *ct,map<uintb,OptimizeRecor
|
|||
const OpTpl *op = ops[i];
|
||||
for(uint4 j=0;j<op->numInput();++j) {
|
||||
const VarnodeTpl *vnin = op->getIn(j);
|
||||
examineVn(recs,vnin,i,j,secnum);
|
||||
examineVn(state,vnin,i,j,secnum);
|
||||
}
|
||||
const VarnodeTpl *vn = op->getOut();
|
||||
examineVn(recs,vn,i,-1,secnum);
|
||||
examineVn(state,vn,i,-1,secnum);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1284,9 +1438,9 @@ void ConsistencyChecker::optimizeGather1(Constructor *ct,map<uintb,OptimizeRecor
|
|||
/// for the section, and if it involves a temporary, mark it as both read and written, guaranteeing
|
||||
/// that the Varnode is not optimized away.
|
||||
/// \param ct is the given Constructor
|
||||
/// \param recs is the collection of count records
|
||||
/// \param state is the collection of count records
|
||||
/// \param secnum is the given p-code section number
|
||||
void ConsistencyChecker::optimizeGather2(Constructor *ct,map<uintb,OptimizeRecord> &recs,int4 secnum) const
|
||||
void ConsistencyChecker::optimizeGather2(Constructor *ct,UniqueState &state,int4 secnum) const
|
||||
|
||||
{
|
||||
ConstructTpl *tpl;
|
||||
|
@ -1300,29 +1454,29 @@ void ConsistencyChecker::optimizeGather2(Constructor *ct,map<uintb,OptimizeRecor
|
|||
if (hand == (HandleTpl *)0) return;
|
||||
if (hand->getPtrSpace().isUniqueSpace()) {
|
||||
if (hand->getPtrOffset().getType() == ConstTpl::real) {
|
||||
pair<map<uintb,OptimizeRecord>::iterator,bool> res;
|
||||
uintb offset = hand->getPtrOffset().getReal();
|
||||
res = recs.insert( pair<uintb,OptimizeRecord>(offset,OptimizeRecord()));
|
||||
(*res.first).second.writeop = 0;
|
||||
(*res.first).second.readop = 0;
|
||||
(*res.first).second.writecount = 2;
|
||||
(*res.first).second.readcount = 2;
|
||||
(*res.first).second.readsection = -2;
|
||||
(*res.first).second.writesection = -2;
|
||||
int4 size = hand->getPtrSize().getReal();
|
||||
vector<OptimizeRecord*> defs;
|
||||
state.getDefinitions(defs,offset,size);
|
||||
for (vector<OptimizeRecord*>::iterator iter=defs.begin();iter!=defs.end();++iter) {
|
||||
(*iter)->updateExport();
|
||||
// NOTE: Could this just be updateRead?
|
||||
// Technically, an exported handle could be written by the parent....
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hand->getSpace().isUniqueSpace()) {
|
||||
if ((hand->getPtrSpace().getType() == ConstTpl::real)&&
|
||||
(hand->getPtrOffset().getType() == ConstTpl::real)) {
|
||||
pair<map<uintb,OptimizeRecord>::iterator,bool> res;
|
||||
uintb offset = hand->getPtrOffset().getReal();
|
||||
res = recs.insert( pair<uintb,OptimizeRecord>(offset,OptimizeRecord()));
|
||||
(*res.first).second.writeop = 0;
|
||||
(*res.first).second.readop = 0;
|
||||
(*res.first).second.writecount = 2;
|
||||
(*res.first).second.readcount = 2;
|
||||
(*res.first).second.readsection = -2;
|
||||
(*res.first).second.writesection = -2;
|
||||
int4 size = hand->getPtrSize().getReal();
|
||||
vector<OptimizeRecord*> defs;
|
||||
state.getDefinitions(defs,offset,size);
|
||||
for (vector<OptimizeRecord*>::iterator iter=defs.begin();iter!=defs.end();++iter) {
|
||||
(*iter)->updateExport();
|
||||
// NOTE: Could this just be updateRead?
|
||||
// Technically, an exported handle could be written by the parent....
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1336,14 +1490,14 @@ void ConsistencyChecker::optimizeGather2(Constructor *ct,map<uintb,OptimizeRecor
|
|||
/// if propagation is forward, the Varnode must not cross another write.
|
||||
/// If all the requirements pass, return the record indicating that the COPY can be removed.
|
||||
/// \param ct is the Constructor owning the p-code
|
||||
/// \param recs is the collection of OptimizeRecords to search
|
||||
/// \param state is the collection of OptimizeRecords to search
|
||||
/// \return a passing OptimizeRecord or null
|
||||
const ConsistencyChecker::OptimizeRecord *ConsistencyChecker::findValidRule(Constructor *ct,
|
||||
const map<uintb,OptimizeRecord> &recs) const
|
||||
const UniqueState &state) const
|
||||
{
|
||||
map<uintb,OptimizeRecord>::const_iterator iter;
|
||||
iter = recs.begin();
|
||||
while(iter != recs.end()) {
|
||||
iter = state.begin();
|
||||
while(iter!=state.end()) {
|
||||
const OptimizeRecord &currec( (*iter).second );
|
||||
++iter;
|
||||
if ((currec.writecount==1)&&(currec.readcount==1)&&(currec.readsection==currec.writesection)) {
|
||||
|
@ -1354,13 +1508,27 @@ const ConsistencyChecker::OptimizeRecord *ConsistencyChecker::findValidRule(Cons
|
|||
else
|
||||
tpl = ct->getNamedTempl(currec.readsection);
|
||||
const vector<OpTpl *> &ops( tpl->getOpvec() );
|
||||
const OpTpl *op = ops[ currec.readop ];
|
||||
const OpTpl *writeop = ops[ currec.writeop ];
|
||||
const OpTpl *readop = ops[ currec.readop ];
|
||||
if (currec.writeop >= currec.readop) // Read must come after write
|
||||
throw SleighError("Read of temporary before write");
|
||||
if (op->getOpcode() == CPUI_COPY) {
|
||||
|
||||
VarnodeTpl *writevn = writeop->getOut();
|
||||
VarnodeTpl *readvn = readop->getIn(currec.inslot);
|
||||
// Because the record can change size and position, we have to check if the varnode
|
||||
// "connecting" the write and read ops is actually the same varnode. If not, then we can't
|
||||
// optimize it out.
|
||||
// There may be an opportunity here to re-write the size/offset when either the write or read
|
||||
// op is a COPY, but I'll leave that for later discussion.
|
||||
// Actually, maybe not. If the truncate would be of a handle, we can't.
|
||||
if (*writevn != *readvn) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (readop->getOpcode() == CPUI_COPY) {
|
||||
bool saverecord = true;
|
||||
currec.opttype = 0; // Read op is a COPY
|
||||
const VarnodeTpl *vn = op->getOut();
|
||||
const VarnodeTpl *vn = readop->getOut();
|
||||
for(int4 i=currec.writeop+1;i<currec.readop;++i) { // Check for interference between write and read
|
||||
if (readWriteInterference(vn,ops[i],true)) {
|
||||
saverecord = false;
|
||||
|
@ -1370,11 +1538,10 @@ const ConsistencyChecker::OptimizeRecord *ConsistencyChecker::findValidRule(Cons
|
|||
if (saverecord)
|
||||
return &currec;
|
||||
}
|
||||
op = ops[ currec.writeop ];
|
||||
if (op->getOpcode() == CPUI_COPY) {
|
||||
if (writeop->getOpcode() == CPUI_COPY) {
|
||||
bool saverecord = true;
|
||||
currec.opttype = 1; // Write op is a COPY
|
||||
const VarnodeTpl *vn = op->getIn(0);
|
||||
const VarnodeTpl *vn = writeop->getIn(0);
|
||||
for(int4 i=currec.writeop+1;i<currec.readop;++i) { // Check for interference between write and read
|
||||
if (readWriteInterference(vn,ops[i],false)) {
|
||||
saverecord = false;
|
||||
|
@ -1431,13 +1598,13 @@ void ConsistencyChecker::applyOptimization(Constructor *ct,const OptimizeRecord
|
|||
/// An error message is issued if a temporary is read but not written.
|
||||
/// A warning may be issued if a temporary is written but not read.
|
||||
/// \param ct is the Constructor
|
||||
/// \param recs is the collection of records associated with each temporary Varnode
|
||||
void ConsistencyChecker::checkUnusedTemps(Constructor *ct,const map<uintb,OptimizeRecord> &recs)
|
||||
/// \param state is the collection of records associated with each temporary Varnode
|
||||
void ConsistencyChecker::checkUnusedTemps(Constructor *ct,const UniqueState &state)
|
||||
|
||||
{
|
||||
map<uintb,OptimizeRecord>::const_iterator iter;
|
||||
iter = recs.begin();
|
||||
while(iter != recs.end()) {
|
||||
iter = state.begin();
|
||||
while(iter != state.end()) {
|
||||
const OptimizeRecord &currec( (*iter).second );
|
||||
if (currec.readcount == 0) {
|
||||
if (printdeadwarning)
|
||||
|
@ -1485,19 +1652,19 @@ void ConsistencyChecker::optimize(Constructor *ct)
|
|||
|
||||
{
|
||||
const OptimizeRecord *currec;
|
||||
map<uintb,OptimizeRecord> recs;
|
||||
UniqueState state;
|
||||
int4 numsections = ct->getNumSections();
|
||||
do {
|
||||
recs.clear();
|
||||
state.clear();
|
||||
for(int4 i=-1;i<numsections;++i) {
|
||||
optimizeGather1(ct,recs,i);
|
||||
optimizeGather2(ct,recs,i);
|
||||
optimizeGather1(ct,state,i);
|
||||
optimizeGather2(ct,state,i);
|
||||
}
|
||||
currec = findValidRule(ct,recs);
|
||||
currec = findValidRule(ct,state);
|
||||
if (currec != (const OptimizeRecord *)0)
|
||||
applyOptimization(ct,*currec);
|
||||
} while(currec != (const OptimizeRecord *)0);
|
||||
checkUnusedTemps(ct,recs);
|
||||
checkUnusedTemps(ct,state);
|
||||
}
|
||||
|
||||
/// Warnings or errors for individual violations may be printed, depending on settings.
|
||||
|
@ -1589,6 +1756,13 @@ void ConsistencyChecker::optimizeAll(void)
|
|||
}
|
||||
}
|
||||
|
||||
ostream& operator<<(ostream &os, const ConsistencyChecker::OptimizeRecord &rec) {
|
||||
os << "{writeop=" << rec.writeop << " readop=" << rec.readop << " inslot=" << rec.inslot <<
|
||||
" writecount=" << rec.writecount << " readcount=" << rec.readcount <<
|
||||
" opttype=" << rec.opttype << "}";
|
||||
return os;
|
||||
}
|
||||
|
||||
/// Sort based on the containing Varnode, then on the bit boundary
|
||||
/// \param op2 is a field to compare with \b this
|
||||
/// \return \b true if \b this should be sorted before the other field
|
||||
|
|
|
@ -23,12 +23,14 @@
|
|||
#include "filemanage.hh"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace ghidra {
|
||||
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
using std::out_of_range;
|
||||
using std::string;
|
||||
|
||||
/// \brief A helper class to associate a \e named Constructor section with its symbol scope
|
||||
///
|
||||
|
@ -137,13 +139,15 @@ class SleighCompile;
|
|||
/// This class searches for unnecessary truncations and extensions, temporary varnodes that are either dead,
|
||||
/// read before written, or that exceed the standard allocation size.
|
||||
class ConsistencyChecker {
|
||||
|
||||
public:
|
||||
/// \brief Description of how a temporary register is being used within a Constructor
|
||||
///
|
||||
/// This counts reads and writes of the register. If the register is read only once, the
|
||||
/// particular p-code op and input slot reading it is recorded. If the register is written
|
||||
/// only once, the particular p-code op writing it is recorded.
|
||||
struct OptimizeRecord {
|
||||
uintb offset; ///< Offset of the varnode address
|
||||
int4 size; ///< Size in bytes of the varnode or piece (immutable)
|
||||
int4 writeop; ///< Index of the (last) p-code op writing to register (or -1)
|
||||
int4 readop; ///< Index of the (last) p-code op reading the register (or -1)
|
||||
int4 inslot; ///< Input slot of p-code op reading the register (or -1)
|
||||
|
@ -154,9 +158,32 @@ class ConsistencyChecker {
|
|||
mutable int4 opttype; ///< 0 = register read by a COPY, 1 = register written by a COPY (-1 otherwise)
|
||||
|
||||
/// \brief Construct a record, initializing counts
|
||||
OptimizeRecord(void) {
|
||||
writeop = -1; readop = -1; inslot=-1; writecount=0; readcount=0; writesection=-2; readsection=-2; opttype=-1; }
|
||||
OptimizeRecord(uintb offset, int4 size) {
|
||||
this->offset = offset;
|
||||
this->size = size;
|
||||
writeop = -1; readop = -1; inslot=-1; writecount=0; readcount=0; writesection=-2; readsection=-2; opttype=-1;
|
||||
}
|
||||
void copyFromExcludingSize(OptimizeRecord &that);
|
||||
void update(int4 opIdx, int4 slotIdx, int4 secNum);
|
||||
void updateRead(int4 i, int4 inslot, int4 secNum);
|
||||
void updateWrite(int4 i, int4 secNum);
|
||||
void updateExport();
|
||||
void updateCombine(OptimizeRecord &that);
|
||||
};
|
||||
private:
|
||||
class UniqueState {
|
||||
map<uintb,OptimizeRecord> recs;
|
||||
static uintb endOf(map<uintb,OptimizeRecord>::iterator &iter) { return iter->first + iter->second.size; }
|
||||
OptimizeRecord coalesce(vector<OptimizeRecord*> &records);
|
||||
map<uintb,OptimizeRecord>::iterator lesserIter(uintb offset);
|
||||
public:
|
||||
void clear(void) { recs.clear(); }
|
||||
void set(uintb offset, int4 size, OptimizeRecord &rec);
|
||||
void getDefinitions(vector<OptimizeRecord*> &result, uintb offset, int4 size);
|
||||
map<uintb,OptimizeRecord>::const_iterator begin(void) const { return recs.begin(); }
|
||||
map<uintb,OptimizeRecord>::const_iterator end(void) const { return recs.end(); }
|
||||
};
|
||||
|
||||
SleighCompile *compiler; ///< Parsed form of the SLEIGH file being examined
|
||||
int4 unnecessarypcode; ///< Count of unnecessary extension/truncation operations
|
||||
int4 readnowrite; ///< Count of temporary registers that are read but not written
|
||||
|
@ -185,14 +212,14 @@ class ConsistencyChecker {
|
|||
void setPostOrder(SubtableSymbol *root);
|
||||
|
||||
// Optimization routines
|
||||
static void examineVn(map<uintb,OptimizeRecord> &recs,const VarnodeTpl *vn,uint4 i,int4 inslot,int4 secnum);
|
||||
static void examineVn(UniqueState &state,const VarnodeTpl *vn,uint4 i,int4 inslot,int4 secnum);
|
||||
static bool possibleIntersection(const VarnodeTpl *vn1,const VarnodeTpl *vn2);
|
||||
bool readWriteInterference(const VarnodeTpl *vn,const OpTpl *op,bool checkread) const;
|
||||
void optimizeGather1(Constructor *ct,map<uintb,OptimizeRecord> &recs,int4 secnum) const;
|
||||
void optimizeGather2(Constructor *ct,map<uintb,OptimizeRecord> &recs,int4 secnum) const;
|
||||
const OptimizeRecord *findValidRule(Constructor *ct,const map<uintb,OptimizeRecord> &recs) const;
|
||||
void optimizeGather1(Constructor *ct,UniqueState &state,int4 secnum) const;
|
||||
void optimizeGather2(Constructor *ct,UniqueState &state,int4 secnum) const;
|
||||
const OptimizeRecord *findValidRule(Constructor *ct,const UniqueState &state) const;
|
||||
void applyOptimization(Constructor *ct,const OptimizeRecord &rec);
|
||||
void checkUnusedTemps(Constructor *ct,const map<uintb,OptimizeRecord> &recs);
|
||||
void checkUnusedTemps(Constructor *ct,const UniqueState &state);
|
||||
void checkLargeTemporaries(Constructor *ct,ConstructTpl *ctpl);
|
||||
void optimize(Constructor *ct);
|
||||
public:
|
||||
|
@ -458,6 +485,8 @@ public:
|
|||
int4 run_compilation(const string &filein,const string &fileout);
|
||||
};
|
||||
|
||||
ostream& operator<<(ostream &os, const ConsistencyChecker::OptimizeRecord &rec);
|
||||
|
||||
extern SleighCompile *slgh; ///< A global reference to the SLEIGH compiler accessible to the parse functions
|
||||
extern int yydebug; ///< Debug state for the SLEIGH parse functions
|
||||
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
package ghidra.pcodeCPort.opcodes;
|
||||
// Names of ops associated with their opcode number
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
// Some of the names have been replaced with special placeholder
|
||||
// ops for the sleigh compiler and interpreter these are as follows:
|
||||
// MULTIEQUAL = BUILD
|
||||
|
@ -24,219 +29,168 @@ package ghidra.pcodeCPort.opcodes;
|
|||
// PTRSUB = CROSSBUILD
|
||||
|
||||
public enum OpCode {
|
||||
DO_NOT_USE_ME_I_AM_ENUM_ELEMENT_ZERO,
|
||||
CPUI_COPY, // Copy one operand to another
|
||||
CPUI_LOAD, // Dereference a pointer into specified space
|
||||
CPUI_STORE, // Store at a pointer into specified space
|
||||
DO_NOT_USE_ME_I_AM_ENUM_ELEMENT_ZERO("BLANK"),
|
||||
CPUI_COPY("COPY"), // Copy one operand to another
|
||||
CPUI_LOAD("LOAD"), // Dereference a pointer into specified space
|
||||
CPUI_STORE("STORE"), // Store at a pointer into specified space
|
||||
|
||||
CPUI_BRANCH, // Always branch
|
||||
CPUI_CBRANCH, // Conditional branch
|
||||
CPUI_BRANCHIND, // An indirect branch (jumptable)
|
||||
CPUI_BRANCH("BRANCH"), // Always branch
|
||||
CPUI_CBRANCH("CBRANCH"), // Conditional branch
|
||||
CPUI_BRANCHIND("BRANCHIND"), // An indirect branch (jumptable)
|
||||
|
||||
CPUI_CALL, // A call with absolute address
|
||||
CPUI_CALLIND, // An indirect call
|
||||
CPUI_CALLOTHER, // Other unusual subroutine calling conventions
|
||||
CPUI_RETURN, // A return from subroutine
|
||||
CPUI_CALL("CALL"), // A call with absolute address
|
||||
CPUI_CALLIND("CALLIND"), // An indirect call
|
||||
CPUI_CALLOTHER("CALLOTHER"), // Other unusual subroutine calling conventions
|
||||
CPUI_RETURN("RETURN"), // A return from subroutine
|
||||
|
||||
// Integer/bit operations
|
||||
|
||||
CPUI_INT_EQUAL, // Return TRUE if operand1 == operand2
|
||||
CPUI_INT_NOTEQUAL, // Return TRUE if operand1 != operand2
|
||||
CPUI_INT_SLESS, // Return TRUE if signed op1 < signed op2
|
||||
CPUI_INT_SLESSEQUAL, // Return TRUE if signed op1 <= signed op2
|
||||
CPUI_INT_LESS, // Return TRUE if unsigned op1 < unsigned op2
|
||||
CPUI_INT_EQUAL("INT_EQUAL"), // Return TRUE if operand1 == operand2
|
||||
CPUI_INT_NOTEQUAL("INT_NOTEQUAL"), // Return TRUE if operand1 != operand2
|
||||
CPUI_INT_SLESS("INT_SLESS"), // Return TRUE if signed op1 < signed op2
|
||||
CPUI_INT_SLESSEQUAL("INT_SLESSEQUAL"), // Return TRUE if signed op1 <= signed op2
|
||||
CPUI_INT_LESS("INT_LESS"), // Return TRUE if unsigned op1 < unsigned op2
|
||||
// This also indicates a borrow on unsigned substraction
|
||||
CPUI_INT_LESSEQUAL, // Return TRUE if unsigned op1 <= unsigned op2
|
||||
CPUI_INT_ZEXT, // Zero extend operand
|
||||
CPUI_INT_SEXT, // Sign extend operand
|
||||
CPUI_INT_ADD, // Unsigned addition of operands of same size
|
||||
CPUI_INT_SUB, // Unsigned subtraction of operands of same size
|
||||
CPUI_INT_CARRY, // TRUE if adding two operands has overflow (carry)
|
||||
CPUI_INT_SCARRY, // TRUE if there is a carry in signed addition of two ops
|
||||
CPUI_INT_SBORROW, // TRUE if there is a borrow in signed subtraction of two ops
|
||||
CPUI_INT_2COMP, // Twos complement (for subtracting) of operand
|
||||
CPUI_INT_NEGATE,
|
||||
CPUI_INT_XOR, // Exclusive OR of two operands of same size
|
||||
CPUI_INT_AND,
|
||||
CPUI_INT_OR,
|
||||
CPUI_INT_LEFT, // Left shift
|
||||
CPUI_INT_RIGHT, // Right shift zero fill
|
||||
CPUI_INT_SRIGHT, // Signed right shift
|
||||
CPUI_INT_MULT, // Integer multiplication
|
||||
CPUI_INT_DIV, // Unsigned integer division
|
||||
CPUI_INT_SDIV, // Signed integer division
|
||||
CPUI_INT_REM, // Unsigned mod (remainder)
|
||||
CPUI_INT_SREM, // Signed mod (remainder)
|
||||
CPUI_INT_LESSEQUAL("INT_LESSEQUAL"), // Return TRUE if unsigned op1 <= unsigned op2
|
||||
CPUI_INT_ZEXT("INT_ZEXT"), // Zero extend operand
|
||||
CPUI_INT_SEXT("INT_SEXT"), // Sign extend operand
|
||||
CPUI_INT_ADD("INT_ADD"), // Unsigned addition of operands of same size
|
||||
CPUI_INT_SUB("INT_SUB"), // Unsigned subtraction of operands of same size
|
||||
CPUI_INT_CARRY("INT_CARRY"), // TRUE if adding two operands has overflow (carry)
|
||||
CPUI_INT_SCARRY("INT_SCARRY"), // TRUE if there is a carry in signed addition of two ops
|
||||
CPUI_INT_SBORROW("INT_SBORROW"), // TRUE if there is a borrow in signed subtraction of two ops
|
||||
CPUI_INT_2COMP("INT_2COMP"), // Twos complement (for subtracting) of operand
|
||||
CPUI_INT_NEGATE("INT_NEGATE"),
|
||||
CPUI_INT_XOR("INT_XOR"), // Exclusive OR of two operands of same size
|
||||
CPUI_INT_AND("INT_AND"),
|
||||
CPUI_INT_OR("INT_OR"),
|
||||
CPUI_INT_LEFT("INT_LEFT"), // Left shift
|
||||
CPUI_INT_RIGHT("INT_RIGHT"), // Right shift zero fill
|
||||
CPUI_INT_SRIGHT("INT_SRIGHT"), // Signed right shift
|
||||
CPUI_INT_MULT("INT_MULT"), // Integer multiplication
|
||||
CPUI_INT_DIV("INT_DIV"), // Unsigned integer division
|
||||
CPUI_INT_SDIV("INT_SDIV"), // Signed integer division
|
||||
CPUI_INT_REM("INT_REM"), // Unsigned mod (remainder)
|
||||
CPUI_INT_SREM("INT_SREM"), // Signed mod (remainder)
|
||||
|
||||
CPUI_BOOL_NEGATE, // Boolean negate or not
|
||||
CPUI_BOOL_XOR, // Boolean xor
|
||||
CPUI_BOOL_AND, // Boolean and (&&)
|
||||
CPUI_BOOL_OR, // Boolean or (||)
|
||||
CPUI_BOOL_NEGATE("BOOL_NEGATE"), // Boolean negate or not
|
||||
CPUI_BOOL_XOR("BOOL_XOR"), // Boolean xor
|
||||
CPUI_BOOL_AND("BOOL_AND"), // Boolean and (&&)
|
||||
CPUI_BOOL_OR("BOOL_OR"), // Boolean or (||)
|
||||
|
||||
// Floating point operations
|
||||
|
||||
CPUI_FLOAT_EQUAL, // Return TRUE if operand1 == operand2
|
||||
CPUI_FLOAT_NOTEQUAL, // Return TRUE if operand1 != operand2
|
||||
CPUI_FLOAT_LESS, // Return TRUE if op1 < op2
|
||||
CPUI_FLOAT_LESSEQUAL, // Return TRUE if op1 <= op2
|
||||
CPUI_UNUSED1, // Slot 45 is unused
|
||||
CPUI_FLOAT_NAN, // Return TRUE if op1 is NaN
|
||||
CPUI_FLOAT_EQUAL("FLOAT_EQUAL"), // Return TRUE if operand1 == operand2
|
||||
CPUI_FLOAT_NOTEQUAL("FLOAT_NOTEQUAL"), // Return TRUE if operand1 != operand2
|
||||
CPUI_FLOAT_LESS("FLOAT_LESS"), // Return TRUE if op1 < op2
|
||||
CPUI_FLOAT_LESSEQUAL("FLOAT_LESSEQUAL"), // Return TRUE if op1 <= op2
|
||||
CPUI_UNUSED1("UNUSED1"), // Slot 45 is unused
|
||||
CPUI_FLOAT_NAN("FLOAT_NAN"), // Return TRUE if op1 is NaN
|
||||
|
||||
CPUI_FLOAT_ADD, // float addition
|
||||
CPUI_FLOAT_DIV, // float division
|
||||
CPUI_FLOAT_MULT, // float multiplication
|
||||
CPUI_FLOAT_SUB, // float subtraction
|
||||
CPUI_FLOAT_NEG, // float negation
|
||||
CPUI_FLOAT_ABS, // float absolute value
|
||||
CPUI_FLOAT_SQRT, // float square root
|
||||
CPUI_FLOAT_ADD("FLOAT_ADD"), // float addition
|
||||
CPUI_FLOAT_DIV("FLOAT_DIV"), // float division
|
||||
CPUI_FLOAT_MULT("FLOAT_MULT"), // float multiplication
|
||||
CPUI_FLOAT_SUB("FLOAT_SUB"), // float subtraction
|
||||
CPUI_FLOAT_NEG("FLOAT_NEG"), // float negation
|
||||
CPUI_FLOAT_ABS("FLOAT_ABS"), // float absolute value
|
||||
CPUI_FLOAT_SQRT("FLOAT_SQRT"), // float square root
|
||||
|
||||
CPUI_FLOAT_INT2FLOAT, // convert int type to float type
|
||||
CPUI_FLOAT_FLOAT2FLOAT, // convert between float sizes
|
||||
CPUI_FLOAT_TRUNC, // round towards zero
|
||||
CPUI_FLOAT_CEIL, // round towards +infinity
|
||||
CPUI_FLOAT_FLOOR, // round towards -infinity
|
||||
CPUI_FLOAT_ROUND, // round towards nearest
|
||||
CPUI_FLOAT_INT2FLOAT("INT2FLOAT"), // convert int type to float type
|
||||
CPUI_FLOAT_FLOAT2FLOAT("FLOAT2FLOAT"), // convert between float sizes
|
||||
CPUI_FLOAT_TRUNC("TRUNC"), // round towards zero
|
||||
CPUI_FLOAT_CEIL("CEIL"), // round towards +infinity
|
||||
CPUI_FLOAT_FLOOR("FLOOR"), // round towards -infinity
|
||||
CPUI_FLOAT_ROUND("ROUND"), // round towards nearest
|
||||
|
||||
// Internal opcodes for simplification. Not
|
||||
// typically generated in a direct translation.
|
||||
|
||||
// Dataflow operations
|
||||
CPUI_MULTIEQUAL, // Output is equal to one of its inputs, depending on execution // BUILD
|
||||
CPUI_INDIRECT, // Output probably equals input but may be indirectly affected // DELAY_SLOT
|
||||
CPUI_PIECE, // Output is constructed from multiple pieces
|
||||
CPUI_SUBPIECE, // Output is a subpiece of input0, input1=offset into input0
|
||||
CPUI_MULTIEQUAL("BUILD"), // Output is equal to one of its inputs, depending on execution
|
||||
CPUI_INDIRECT("DELAY_SLOT"), // Output probably equals input but may be indirectly affected
|
||||
CPUI_PIECE("PIECE"), // Output is constructed from multiple pieces
|
||||
CPUI_SUBPIECE("SUBPIECE"), // Output is a subpiece of input0, input1=offset into input0
|
||||
|
||||
CPUI_CAST, // Cast from one type to another // MACROBUILD
|
||||
CPUI_PTRADD, // outptr = ptrbase, offset, (size multiplier) // LABELBUILD
|
||||
CPUI_PTRSUB, // outptr = &(ptr->subfield) // CROSSBUILD
|
||||
CPUI_SEGMENTOP,
|
||||
CPUI_CPOOLREF,
|
||||
CPUI_NEW,
|
||||
CPUI_INSERT,
|
||||
CPUI_EXTRACT,
|
||||
CPUI_POPCOUNT,
|
||||
CPUI_LZCOUNT,
|
||||
CPUI_CAST("CAST"), // Cast from one type to another // MACROBUILD
|
||||
CPUI_PTRADD("LABEL"), // outptr = ptrbase, offset, (size multiplier)
|
||||
CPUI_PTRSUB("CROSSBUILD"), // outptr = &(ptr->subfield)
|
||||
CPUI_SEGMENTOP("SEGMENTOP"),
|
||||
CPUI_CPOOLREF("CPOOLREF"),
|
||||
CPUI_NEW("NEW"),
|
||||
CPUI_INSERT("INSERT"),
|
||||
CPUI_EXTRACT("EXTRACT"),
|
||||
CPUI_POPCOUNT("POPCOUNT"),
|
||||
CPUI_LZCOUNT("LZCOUNT"),
|
||||
|
||||
CPUI_MAX;
|
||||
CPUI_MAX(null);
|
||||
|
||||
private OpCode() {
|
||||
private final String name;
|
||||
|
||||
private OpCode(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return get_opname(this);
|
||||
return name;
|
||||
}
|
||||
|
||||
public OpCode getOpCodeFlip()
|
||||
|
||||
{ // Return the complimentary opcode for boolean operations
|
||||
// (or CPUI_MAX if not boolean) Set reorder to true if
|
||||
// the complimentary operation would involve reordering
|
||||
// the input parameters
|
||||
switch (this) {
|
||||
case CPUI_INT_EQUAL:
|
||||
return CPUI_INT_NOTEQUAL;
|
||||
case CPUI_INT_NOTEQUAL:
|
||||
return CPUI_INT_EQUAL;
|
||||
case CPUI_INT_SLESS:
|
||||
return CPUI_INT_SLESSEQUAL;
|
||||
case CPUI_INT_SLESSEQUAL:
|
||||
return CPUI_INT_SLESS;
|
||||
case CPUI_INT_LESS:
|
||||
return CPUI_INT_LESSEQUAL;
|
||||
case CPUI_INT_LESSEQUAL:
|
||||
return CPUI_INT_LESS;
|
||||
case CPUI_BOOL_NEGATE:
|
||||
return CPUI_COPY;
|
||||
case CPUI_FLOAT_EQUAL:
|
||||
return CPUI_FLOAT_NOTEQUAL;
|
||||
case CPUI_FLOAT_NOTEQUAL:
|
||||
return CPUI_FLOAT_EQUAL;
|
||||
case CPUI_FLOAT_LESS:
|
||||
return CPUI_FLOAT_LESSEQUAL;
|
||||
case CPUI_FLOAT_LESSEQUAL:
|
||||
return CPUI_FLOAT_LESS;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return CPUI_MAX;
|
||||
/**
|
||||
* {@return the complimentary opcode for boolean operations}
|
||||
*
|
||||
* (or {@link #CPUI_MAX} if not boolean.) Set reorder to true if the complimentary operation
|
||||
* would involve reordering the input parameters
|
||||
*/
|
||||
public OpCode getOpCodeFlip() {
|
||||
return switch (this) {
|
||||
case CPUI_INT_EQUAL -> CPUI_INT_NOTEQUAL;
|
||||
case CPUI_INT_NOTEQUAL -> CPUI_INT_EQUAL;
|
||||
case CPUI_INT_SLESS -> CPUI_INT_SLESSEQUAL;
|
||||
case CPUI_INT_SLESSEQUAL -> CPUI_INT_SLESS;
|
||||
case CPUI_INT_LESS -> CPUI_INT_LESSEQUAL;
|
||||
case CPUI_INT_LESSEQUAL -> CPUI_INT_LESS;
|
||||
case CPUI_BOOL_NEGATE -> CPUI_COPY;
|
||||
case CPUI_FLOAT_EQUAL -> CPUI_FLOAT_NOTEQUAL;
|
||||
case CPUI_FLOAT_NOTEQUAL -> CPUI_FLOAT_EQUAL;
|
||||
case CPUI_FLOAT_LESS -> CPUI_FLOAT_LESSEQUAL;
|
||||
case CPUI_FLOAT_LESSEQUAL -> CPUI_FLOAT_LESS;
|
||||
default -> CPUI_MAX;
|
||||
};
|
||||
}
|
||||
|
||||
public boolean getBooleanFlip()
|
||||
|
||||
{ // Return the complimentary opcode for boolean operations
|
||||
// (or CPUI_MAX if not boolean) Set reorder to true if
|
||||
// the complimentary operation would involve reordering
|
||||
// the input parameters
|
||||
switch (this) {
|
||||
case CPUI_INT_EQUAL:
|
||||
return false;
|
||||
case CPUI_INT_NOTEQUAL:
|
||||
return false;
|
||||
case CPUI_INT_SLESS:
|
||||
return true;
|
||||
case CPUI_INT_SLESSEQUAL:
|
||||
return true;
|
||||
case CPUI_INT_LESS:
|
||||
return true;
|
||||
case CPUI_INT_LESSEQUAL:
|
||||
return true;
|
||||
case CPUI_BOOL_NEGATE:
|
||||
return false;
|
||||
case CPUI_FLOAT_EQUAL:
|
||||
return false;
|
||||
case CPUI_FLOAT_NOTEQUAL:
|
||||
return false;
|
||||
case CPUI_FLOAT_LESS:
|
||||
return true;
|
||||
case CPUI_FLOAT_LESSEQUAL:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
/**
|
||||
* {@return the complimentary opcode for boolean operations}
|
||||
*
|
||||
* (or {@link #CPUI_MAX} if not boolean.) Set reorder to true if the complimentary operation
|
||||
* would involve reordering the input parameters
|
||||
*/
|
||||
public boolean getBooleanFlip() {
|
||||
return switch (this) {
|
||||
case CPUI_INT_EQUAL -> false;
|
||||
case CPUI_INT_NOTEQUAL -> false;
|
||||
case CPUI_INT_SLESS -> true;
|
||||
case CPUI_INT_SLESSEQUAL -> true;
|
||||
case CPUI_INT_LESS -> true;
|
||||
case CPUI_INT_LESSEQUAL -> true;
|
||||
case CPUI_BOOL_NEGATE -> false;
|
||||
case CPUI_FLOAT_EQUAL -> false;
|
||||
case CPUI_FLOAT_NOTEQUAL -> false;
|
||||
case CPUI_FLOAT_LESS -> true;
|
||||
case CPUI_FLOAT_LESSEQUAL -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
static final String opcode_name[] = { "BLANK", "COPY", "LOAD", "STORE", "BRANCH", "CBRANCH",
|
||||
"BRANCHIND", "CALL", "CALLIND", "CALLOTHER", "RETURN", "INT_EQUAL", "INT_NOTEQUAL",
|
||||
"INT_SLESS", "INT_SLESSEQUAL", "INT_LESS", "INT_LESSEQUAL", "INT_ZEXT", "INT_SEXT",
|
||||
"INT_ADD", "INT_SUB", "INT_CARRY", "INT_SCARRY", "INT_SBORROW", "INT_2COMP", "INT_NEGATE",
|
||||
"INT_XOR", "INT_AND", "INT_OR", "INT_LEFT", "INT_RIGHT", "INT_SRIGHT", "INT_MULT",
|
||||
"INT_DIV", "INT_SDIV", "INT_REM", "INT_SREM", "BOOL_NEGATE", "BOOL_XOR", "BOOL_AND",
|
||||
"BOOL_OR", "FLOAT_EQUAL", "FLOAT_NOTEQUAL", "FLOAT_LESS", "FLOAT_LESSEQUAL", "UNUSED1",
|
||||
"FLOAT_NAN", "FLOAT_ADD", "FLOAT_DIV", "FLOAT_MULT", "FLOAT_SUB", "FLOAT_NEG", "FLOAT_ABS",
|
||||
"FLOAT_SQRT", "INT2FLOAT", "FLOAT2FLOAT", "TRUNC", "CEIL", "FLOOR", "ROUND", "BUILD",
|
||||
"DELAY_SLOT", "PIECE", "SUBPIECE", "CAST", "LABEL", "CROSSBUILD", "SEGMENTOP", "CPOOLREF",
|
||||
"NEW", "INSERT", "EXTRACT", "POPCOUNT", "LZCOUNT" };
|
||||
static final List<OpCode> opsByOrdinal = List.of(OpCode.values());
|
||||
static final Map<String, OpCode> opsByName =
|
||||
Stream.of(OpCode.values())
|
||||
.filter(op -> op != DO_NOT_USE_ME_I_AM_ENUM_ELEMENT_ZERO && op != CPUI_MAX)
|
||||
.collect(Collectors.toUnmodifiableMap(OpCode::getName, op -> op));
|
||||
|
||||
public static String get_opname(OpCode op) {
|
||||
return opcode_name[op.ordinal()];
|
||||
public static OpCode getOpcode(int ordinal) {
|
||||
return opsByOrdinal.get(ordinal);
|
||||
}
|
||||
|
||||
static final int opcode_indices[] = { 0, 39, 37, 40, 38, 4, 6, 60, 7, 8, 9, 64, 5, 57, 1, 68,
|
||||
66, 61, 71, 55, 52, 47, 48, 41, 43, 44, 49, 46, 51, 42, 53, 50, 58, 70, 54, 24, 19, 27, 21,
|
||||
33, 11, 29, 15, 16, 32, 25, 12, 28, 35, 30, 23, 22, 34, 18, 13, 14, 36, 31, 20, 26, 17, 65,
|
||||
2, 73, 69, 62, 72, 10, 59, 67, 3, 63, 56, 45 };
|
||||
|
||||
public static OpCode get_opcode(String nm) { // Use binary search to find name
|
||||
int min = 1; // Don't include BLANK
|
||||
int max = OpCode.CPUI_MAX.ordinal() - 1;
|
||||
int cur, ind;
|
||||
|
||||
while (min <= max) { // Binary search
|
||||
cur = (min + max) / 2;
|
||||
ind = opcode_indices[cur]; // Get opcode in cur's sort slot
|
||||
int result = opcode_name[ind].compareTo(nm);
|
||||
if (result < 0) {
|
||||
min = cur + 1; // Everything equal or below cur is less
|
||||
}
|
||||
else if (result > 0) {
|
||||
max = cur - 1; // Everything equal or above cur is greater
|
||||
}
|
||||
else {
|
||||
return OpCode.values()[ind]; // Found the match
|
||||
public static OpCode getOpcode(String nm) {
|
||||
return opsByName.get(nm);
|
||||
}
|
||||
}
|
||||
return null; // Name isn't an op
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,8 +28,13 @@ import ghidra.program.model.pcode.Encoder;
|
|||
public class ConstTpl {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{type=" + type + " value_real=" + String.format("0x%x", value_real) + " spaceid=" +
|
||||
spaceid + "}";
|
||||
return switch (type) {
|
||||
case real -> "ConstTpl[real=0x%x]".formatted(value_real);
|
||||
case handle -> "ConstTpl[handle=%d,sel=%s]".formatted(handle_index, select);
|
||||
case spaceid -> "ConstTpl[space=%s]".formatted(spaceid);
|
||||
default -> "ConstTpl[type=%s,real=0x%x,space=%s,handle=%d,sel=%s]".formatted(type,
|
||||
value_real, spaceid, handle_index, select);
|
||||
};
|
||||
}
|
||||
|
||||
public enum const_type {
|
||||
|
|
|
@ -44,7 +44,7 @@ public class OpTpl {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OpTpl[opcode=" + opc + "]";
|
||||
return "OpTpl[%s = %s %s]".formatted(output, opc, input);
|
||||
}
|
||||
|
||||
public VarnodeTpl getOut() {
|
||||
|
@ -129,7 +129,7 @@ public class OpTpl {
|
|||
|
||||
public void encode(Encoder encoder) throws IOException {
|
||||
encoder.openElement(ELEM_OP_TPL);
|
||||
encoder.writeOpcode(ATTRIB_CODE, opc.ordinal());
|
||||
encoder.writeOpcode(ATTRIB_CODE, opc);
|
||||
if (output == null) {
|
||||
encoder.openElement(ELEM_NULL);
|
||||
encoder.closeElement(ELEM_NULL);
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
package ghidra.pcodeCPort.semantics;
|
||||
|
||||
import static ghidra.pcode.utils.SlaFormat.*;
|
||||
import static ghidra.pcode.utils.SlaFormat.ELEM_VARNODE_TPL;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -210,13 +210,14 @@ public class VarnodeTpl {
|
|||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
if (!(obj instanceof VarnodeTpl op2)) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof VarnodeTpl)) {
|
||||
return false;
|
||||
return space.equals(op2.space) && offset.equals(op2.offset) && size.equals(op2.size);
|
||||
}
|
||||
VarnodeTpl o2 = (VarnodeTpl) obj;
|
||||
return space.equals(o2.space) && offset.equals(o2.offset) && size.equals(o2.size);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[space=%s:offset=%s:size=%s]".formatted(space, offset, size);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,11 @@
|
|||
*/
|
||||
package ghidra.pcodeCPort.slgh_compile;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import generic.stl.*;
|
||||
import generic.stl.IteratorSTL;
|
||||
import generic.stl.VectorSTL;
|
||||
import ghidra.pcodeCPort.context.SleighError;
|
||||
import ghidra.pcodeCPort.opcodes.OpCode;
|
||||
import ghidra.pcodeCPort.semantics.*;
|
||||
|
@ -39,10 +41,10 @@ class ConsistencyChecker {
|
|||
private boolean printlargetempwarning; // if true, warning about temporary varnodes larger than SleighBase.MAX_UNIQUE_SIZE
|
||||
private SleighCompile compiler;
|
||||
private SubtableSymbol root_symbol;
|
||||
private VectorSTL<SubtableSymbol> postorder = new VectorSTL<>();
|
||||
private List<SubtableSymbol> postorder = new ArrayList<>();
|
||||
|
||||
// Sizes associated with tables
|
||||
private MapSTL<SubtableSymbol, Integer> sizemap = new MapSTL<>((s1, s2) -> s1.compareTo(s2));
|
||||
private Map<SubtableSymbol, Integer> sizemap = new HashMap<>();
|
||||
|
||||
private OperandSymbol getOperandSymbol(int slot, OpTpl op, Constructor ct) {
|
||||
VarnodeTpl vn;
|
||||
|
@ -519,37 +521,30 @@ class ConsistencyChecker {
|
|||
}
|
||||
|
||||
private int recoverSize(ConstTpl sizeconst, Constructor ct) {
|
||||
int size = 0, handindex;
|
||||
OperandSymbol opsym;
|
||||
SubtableSymbol tabsym;
|
||||
IteratorSTL<Pair<SubtableSymbol, Integer>> iter;
|
||||
return switch (sizeconst.getType()) {
|
||||
case real -> (int) sizeconst.getReal();
|
||||
case handle -> {
|
||||
int handindex = sizeconst.getHandleIndex();
|
||||
OperandSymbol opsym = ct.getOperand(handindex);
|
||||
int size = opsym.getSize();
|
||||
if (size != -1) {
|
||||
yield size;
|
||||
}
|
||||
|
||||
switch (sizeconst.getType()) {
|
||||
case real:
|
||||
size = (int) sizeconst.getReal();
|
||||
break;
|
||||
case handle:
|
||||
handindex = sizeconst.getHandleIndex();
|
||||
opsym = ct.getOperand(handindex);
|
||||
size = opsym.getSize();
|
||||
if (size == -1) {
|
||||
TripleSymbol definingSymbol = opsym.getDefiningSymbol();
|
||||
if (!(definingSymbol instanceof SubtableSymbol)) {
|
||||
if (!(definingSymbol instanceof SubtableSymbol tabsym)) {
|
||||
throw new SleighError("Could not recover varnode template size",
|
||||
ct.location);
|
||||
}
|
||||
tabsym = (SubtableSymbol) definingSymbol;
|
||||
iter = sizemap.find(tabsym);
|
||||
if (iter.isEnd()) {
|
||||
Integer symsize = sizemap.get(tabsym);
|
||||
if (symsize == null) {
|
||||
throw new SleighError("Subtable out of order", ct.location);
|
||||
}
|
||||
size = iter.get().second;
|
||||
yield symsize;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new SleighError("Bad constant type as varnode template size", ct.location);
|
||||
}
|
||||
return size;
|
||||
default -> throw new SleighError("Bad constant type as varnode template size",
|
||||
ct.location);
|
||||
};
|
||||
}
|
||||
|
||||
private void handle(String msg, Constructor ct) {
|
||||
|
@ -571,7 +566,7 @@ class ConsistencyChecker {
|
|||
|
||||
private boolean checkOpMisuse(OpTpl op, Constructor ct) {
|
||||
switch (op.getOpcode()) {
|
||||
case CPUI_INT_LESS: {
|
||||
case CPUI_INT_LESS -> {
|
||||
VarnodeTpl vn0 = op.getIn(0);
|
||||
VarnodeTpl vn1 = op.getIn(1);
|
||||
if (vn1.getSpace().isConstSpace()) {
|
||||
|
@ -591,8 +586,7 @@ class ConsistencyChecker {
|
|||
handleBetter("!= 0", ct);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CPUI_INT_LESSEQUAL: {
|
||||
case CPUI_INT_LESSEQUAL -> {
|
||||
VarnodeTpl vn0 = op.getIn(0);
|
||||
VarnodeTpl vn1 = op.getIn(1);
|
||||
if (vn0.getSpace().isConstSpace()) {
|
||||
|
@ -612,9 +606,8 @@ class ConsistencyChecker {
|
|||
handleBetter("== 0", ct);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -640,9 +633,10 @@ class ConsistencyChecker {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true precisely when {@code opTpl} uses a {@link VarnodeTpl} in
|
||||
* the unique space whose size is larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
|
||||
* Note that this method returns as soon as one large {@link VarnodeTpl} is found.
|
||||
* Returns true precisely when {@code opTpl} uses a {@link VarnodeTpl} in the unique space whose
|
||||
* size is larger than {@link SleighBase#MAX_UNIQUE_SIZE}. Note that this method returns as soon
|
||||
* as one large {@link VarnodeTpl} is found.
|
||||
*
|
||||
* @param opTpl the op to check
|
||||
* @return true if {@code opTpl} uses a large temporary varnode
|
||||
*/
|
||||
|
@ -661,8 +655,9 @@ class ConsistencyChecker {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true precisely when {@code vn} is in the unique space
|
||||
* and has a size larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
|
||||
* Returns true precisely when {@code vn} is in the unique space and has a size larger than
|
||||
* {@link SleighBase#MAX_UNIQUE_SIZE}.
|
||||
*
|
||||
* @param vn varnode template to check
|
||||
* @return true if it uses a large temporary
|
||||
*/
|
||||
|
@ -836,7 +831,7 @@ class ConsistencyChecker {
|
|||
path.pop_back(); // Table is fully traversed
|
||||
state.pop_back();
|
||||
ctstate.pop_back();
|
||||
postorder.push_back(cur); // Post the traversed table
|
||||
postorder.add(cur); // Post the traversed table
|
||||
}
|
||||
else {
|
||||
Constructor ct = cur.getConstructor(ctind);
|
||||
|
@ -849,11 +844,9 @@ class ConsistencyChecker {
|
|||
ctstate.setBack(oper + 1);
|
||||
OperandSymbol opsym = ct.getOperand(oper);
|
||||
TripleSymbol definingSymbol = opsym.getDefiningSymbol();
|
||||
if (definingSymbol instanceof SubtableSymbol) {
|
||||
SubtableSymbol subsym = (SubtableSymbol) definingSymbol;
|
||||
IteratorSTL<Pair<SubtableSymbol, Integer>> iter;
|
||||
iter = sizemap.find(subsym);
|
||||
if (iter.isEnd()) { // Not traversed yet
|
||||
if (definingSymbol instanceof SubtableSymbol subsym) {
|
||||
Integer symsize = sizemap.get(subsym);
|
||||
if (symsize == null) { // Not traversed yet
|
||||
sizemap.put(subsym, -1); // Mark table as
|
||||
// traversed
|
||||
path.push_back(subsym); // Recurse
|
||||
|
@ -866,8 +859,97 @@ class ConsistencyChecker {
|
|||
}
|
||||
}
|
||||
|
||||
static class UniqueState {
|
||||
NavigableMap<Long, OptimizeRecord> recs = new TreeMap<>();
|
||||
|
||||
private static long endOf(Entry<Long, OptimizeRecord> entry) {
|
||||
return entry.getKey() + entry.getValue().size;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
recs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine all the given entries into one, where the varnode is the union of all the given
|
||||
* varnodes.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: There may be a weird case where two neighboring varnodes are read, then later they
|
||||
* get coalesced in a write. This would indicate the combined varnode is read twice, which
|
||||
* is not exactly correct. However, the optimizer would exclude that case, anyway.
|
||||
*
|
||||
* @param records the entries
|
||||
* @return the coalesced entry
|
||||
*/
|
||||
private OptimizeRecord coalesce(List<OptimizeRecord> records) {
|
||||
long minOff = -1;
|
||||
long maxOff = -1;
|
||||
for (OptimizeRecord rec : records) {
|
||||
if (minOff == -1 || rec.offset < minOff) {
|
||||
minOff = rec.offset;
|
||||
}
|
||||
if (maxOff == -1 || rec.offset + rec.size > maxOff) {
|
||||
maxOff = rec.offset + rec.size;
|
||||
}
|
||||
}
|
||||
OptimizeRecord result = new OptimizeRecord(minOff, (int) (maxOff - minOff));
|
||||
for (OptimizeRecord rec : records) {
|
||||
result.updateCombine(rec);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void set(long offset, int size, OptimizeRecord rec) {
|
||||
List<OptimizeRecord> records = getDefinitions(offset, size);
|
||||
records.add(rec);
|
||||
OptimizeRecord coalesced = coalesce(records);
|
||||
recs.subMap(coalesced.offset, coalesced.offset + coalesced.size).clear();
|
||||
recs.put(coalesced.offset, coalesced);
|
||||
}
|
||||
|
||||
private List<OptimizeRecord> getDefinitions(long offset, int size) {
|
||||
if (size == 0) {
|
||||
size = 1;
|
||||
}
|
||||
List<OptimizeRecord> result = new ArrayList<>();
|
||||
Entry<Long, OptimizeRecord> preEntry = recs.lowerEntry(offset);
|
||||
long cursor = offset;
|
||||
if (preEntry != null && endOf(preEntry) > offset) {
|
||||
OptimizeRecord preRec = preEntry.getValue();
|
||||
// No need to truncate, as we're just counting a read
|
||||
// Do not overwrite in map for reads
|
||||
cursor = endOf(preEntry);
|
||||
// Make an immutable copy of the entry.
|
||||
result.add(preRec);
|
||||
}
|
||||
long end = offset + size;
|
||||
Map<Long, OptimizeRecord> toPut = new HashMap<>();
|
||||
for (Entry<Long, OptimizeRecord> entry : recs.subMap(offset, end).entrySet()) {
|
||||
if (entry.getKey() > cursor) {
|
||||
// This will certainly cause an error report. Good.
|
||||
OptimizeRecord missing =
|
||||
new OptimizeRecord(cursor, (int) (entry.getKey() - cursor));
|
||||
toPut.put(cursor, missing);
|
||||
result.add(missing);
|
||||
}
|
||||
// No need to truncate, as we're just counting a read
|
||||
result.add(entry.getValue());
|
||||
cursor = endOf(entry);
|
||||
}
|
||||
if (end > cursor) {
|
||||
OptimizeRecord missing = new OptimizeRecord(cursor, (int) (end - cursor));
|
||||
toPut.put(cursor, missing);
|
||||
result.add(missing);
|
||||
}
|
||||
recs.putAll(toPut);
|
||||
assert !result.isEmpty();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Optimization routines
|
||||
private static void examineVn(MapSTL<Long, OptimizeRecord> recs, VarnodeTpl vn, int i,
|
||||
private static void examineVn(UniqueState state, VarnodeTpl vn, int i,
|
||||
int inslot, int secnum) {
|
||||
if (vn == null) {
|
||||
return;
|
||||
|
@ -879,22 +961,17 @@ class ConsistencyChecker {
|
|||
return;
|
||||
}
|
||||
|
||||
IteratorSTL<Pair<Long, OptimizeRecord>> iter;
|
||||
iter = recs.find(vn.getOffset().getReal());
|
||||
if (iter.isEnd()) {
|
||||
recs.put(vn.getOffset().getReal(), new OptimizeRecord());
|
||||
iter = recs.find(vn.getOffset().getReal());
|
||||
}
|
||||
long offset = vn.getOffset().getReal();
|
||||
int size = (int) vn.getSize().getReal();
|
||||
if (inslot >= 0) {
|
||||
iter.get().second.readop = i;
|
||||
iter.get().second.readcount += 1;
|
||||
iter.get().second.inslot = inslot;
|
||||
iter.get().second.readsection = secnum;
|
||||
for (OptimizeRecord rec : state.getDefinitions(offset, size)) {
|
||||
rec.updateRead(i, inslot, secnum);
|
||||
}
|
||||
}
|
||||
else {
|
||||
iter.get().second.writeop = i;
|
||||
iter.get().second.writecount += 1;
|
||||
iter.get().second.writesection = secnum;
|
||||
OptimizeRecord rec = new OptimizeRecord(offset, size);
|
||||
rec.updateWrite(i, secnum);
|
||||
state.set(offset, size, rec);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -997,7 +1074,7 @@ class ConsistencyChecker {
|
|||
}
|
||||
|
||||
// Look for reads and writes to temporaries
|
||||
private void optimizeGather1(Constructor ct, MapSTL<Long, OptimizeRecord> recs, int secnum) {
|
||||
private void optimizeGather1(Constructor ct, UniqueState state, int secnum) {
|
||||
ConstructTpl tpl;
|
||||
if (secnum < 0) {
|
||||
tpl = ct.getTempl();
|
||||
|
@ -1013,15 +1090,15 @@ class ConsistencyChecker {
|
|||
OpTpl op = ops.get(i);
|
||||
for (int j = 0; j < op.numInput(); ++j) {
|
||||
VarnodeTpl vnin = op.getIn(j);
|
||||
examineVn(recs, vnin, i, j, secnum);
|
||||
examineVn(state, vnin, i, j, secnum);
|
||||
}
|
||||
VarnodeTpl vn = op.getOut();
|
||||
examineVn(recs, vn, i, -1, secnum);
|
||||
examineVn(state, vn, i, -1, secnum);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure any temp used by the export is not optimized away
|
||||
private void optimizeGather2(Constructor ct, MapSTL<Long, OptimizeRecord> recs, int secnum) {
|
||||
private void optimizeGather2(Constructor ct, UniqueState state, int secnum) {
|
||||
ConstructTpl tpl;
|
||||
if (secnum < 0) {
|
||||
tpl = ct.getTempl();
|
||||
|
@ -1039,39 +1116,31 @@ class ConsistencyChecker {
|
|||
if (hand.getPtrSpace().isUniqueSpace()) {
|
||||
if (hand.getPtrOffset().getType() == ConstTpl.const_type.real) {
|
||||
long offset = hand.getPtrOffset().getReal();
|
||||
recs.put(offset, new OptimizeRecord());
|
||||
IteratorSTL<Pair<Long, OptimizeRecord>> res = recs.find(offset);
|
||||
res.get().second.writeop = 0;
|
||||
res.get().second.readop = 0;
|
||||
res.get().second.writecount = 2;
|
||||
res.get().second.readcount = 2;
|
||||
res.get().second.readsection = -2;
|
||||
res.get().second.writesection = -2;
|
||||
int size = (int) hand.getPtrSize().getReal();
|
||||
for (OptimizeRecord rec : state.getDefinitions(offset, size)) {
|
||||
rec.updateExport();
|
||||
// NOTE: Could this just be updateRead?
|
||||
// Technically, an exported handle could be written by the parent....
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hand.getSpace().isUniqueSpace()) {
|
||||
if ((hand.getPtrSpace().getType() == ConstTpl.const_type.real) &&
|
||||
(hand.getPtrOffset().getType() == ConstTpl.const_type.real)) {
|
||||
long offset = hand.getPtrOffset().getReal();
|
||||
recs.put(offset, new OptimizeRecord());
|
||||
IteratorSTL<Pair<Long, OptimizeRecord>> res = recs.find(offset);
|
||||
res.get().second.writeop = 0;
|
||||
res.get().second.readop = 0;
|
||||
res.get().second.writecount = 2;
|
||||
res.get().second.readcount = 2;
|
||||
res.get().second.readsection = -2;
|
||||
res.get().second.writesection = -2;
|
||||
int size = (int) hand.getPtrSize().getReal();
|
||||
for (OptimizeRecord rec : state.getDefinitions(offset, size)) {
|
||||
rec.updateExport();
|
||||
// NOTE: Could this just be updateRead?
|
||||
// Technically, an exported handle could be written by the parent....
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private OptimizeRecord findValidRule(Constructor ct, MapSTL<Long, OptimizeRecord> recs) {
|
||||
IteratorSTL<Pair<Long, OptimizeRecord>> iter;
|
||||
iter = recs.begin();
|
||||
while (!iter.isEnd()) {
|
||||
OptimizeRecord currec = iter.get().second;
|
||||
iter.increment();
|
||||
|
||||
private OptimizeRecord findValidRule(Constructor ct, UniqueState state) {
|
||||
for (Entry<Long, OptimizeRecord> ent : state.recs.entrySet()) {
|
||||
OptimizeRecord currec = ent.getValue();
|
||||
if ((currec.writecount == 1) && (currec.readcount == 1) &&
|
||||
(currec.readsection == currec.writesection)) {
|
||||
// Temporary must be read and written exactly once
|
||||
|
@ -1083,14 +1152,33 @@ class ConsistencyChecker {
|
|||
tpl = ct.getNamedTempl(currec.readsection);
|
||||
}
|
||||
VectorSTL<OpTpl> ops = tpl.getOpvec();
|
||||
OpTpl op = ops.get(currec.readop);
|
||||
OpTpl writeop = ops.get(currec.writeop);
|
||||
OpTpl readop = ops.get(currec.readop);
|
||||
if (currec.writeop >= currec.readop) {
|
||||
throw new SleighError("Read of temporary before write", ct.location);
|
||||
}
|
||||
if (op.getOpcode() == OpCode.CPUI_COPY) {
|
||||
|
||||
VarnodeTpl writevn = writeop.getOut();
|
||||
VarnodeTpl readvn = readop.getIn(currec.inslot);
|
||||
|
||||
/**
|
||||
* Because the record can change size and position, we have to check if the varnode
|
||||
* "connecting" the write and read ops is actually the same varnode. If not, then we
|
||||
* can't optimize it out.
|
||||
*
|
||||
* There may be an opportunity here to re-write the size/offset when either the
|
||||
* write or read op is a COPY, but I'll leave that for later discussion.
|
||||
*
|
||||
* Actually, maybe not. If the truncation would be of a handle, we can't.
|
||||
*/
|
||||
if (!Objects.equals(writevn, readvn)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (readop.getOpcode() == OpCode.CPUI_COPY) {
|
||||
boolean saverecord = true;
|
||||
currec.opttype = 0;
|
||||
VarnodeTpl vn = op.getOut();
|
||||
VarnodeTpl vn = readop.getOut();
|
||||
for (int i = currec.writeop + 1; i < currec.readop; ++i) {
|
||||
if (readWriteInterference(vn, ops.get(i), true)) {
|
||||
saverecord = false;
|
||||
|
@ -1101,11 +1189,10 @@ class ConsistencyChecker {
|
|||
return currec;
|
||||
}
|
||||
}
|
||||
op = ops.get(currec.writeop);
|
||||
if (op.getOpcode() == OpCode.CPUI_COPY) {
|
||||
if (writeop.getOpcode() == OpCode.CPUI_COPY) {
|
||||
boolean saverecord = true;
|
||||
currec.opttype = 1;
|
||||
VarnodeTpl vn = op.getIn(0);
|
||||
VarnodeTpl vn = writeop.getIn(0);
|
||||
for (int i = currec.writeop + 1; i < currec.readop; ++i) {
|
||||
if (readWriteInterference(vn, ops.get(i), false)) {
|
||||
saverecord = false;
|
||||
|
@ -1148,11 +1235,8 @@ class ConsistencyChecker {
|
|||
ctempl.deleteOps(deleteops);
|
||||
}
|
||||
|
||||
private void checkUnusedTemps(Constructor ct, MapSTL<Long, OptimizeRecord> recs) {
|
||||
IteratorSTL<Pair<Long, OptimizeRecord>> iter = recs.begin();
|
||||
while (!iter.isEnd()) {
|
||||
Pair<Long, OptimizeRecord> pair = iter.get();
|
||||
OptimizeRecord currec = pair.second;
|
||||
private void checkUnusedTemps(Constructor ct, UniqueState state) {
|
||||
for (OptimizeRecord currec : state.recs.values()) {
|
||||
if (currec.readcount == 0) {
|
||||
if (printdeadwarning) {
|
||||
compiler.reportWarning(ct.location, "Temporary is written but not read");
|
||||
|
@ -1163,13 +1247,13 @@ class ConsistencyChecker {
|
|||
compiler.reportError(ct.location, "Temporary is read but not written");
|
||||
readnowrite += 1;
|
||||
}
|
||||
iter.increment();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks {@code ct} to see whether p-code section contains an {@link OpTpl} which
|
||||
* uses a varnode in the unique space which is larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
|
||||
* Checks {@code ct} to see whether p-code section contains an {@link OpTpl} which uses a
|
||||
* varnode in the unique space which is larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
|
||||
*
|
||||
* @param ct constructor to check
|
||||
* @param ctpl is the specific p-code section
|
||||
*/
|
||||
|
@ -1190,21 +1274,21 @@ class ConsistencyChecker {
|
|||
|
||||
private void optimize(Constructor ct) {
|
||||
OptimizeRecord currec;
|
||||
MapSTL<Long, OptimizeRecord> recs = new ComparableMapSTL<>();
|
||||
UniqueState state = new UniqueState();
|
||||
int numsections = ct.getNumSections();
|
||||
do {
|
||||
recs.clear();
|
||||
state.clear();
|
||||
for (int i = -1; i < numsections; ++i) {
|
||||
optimizeGather1(ct, recs, i);
|
||||
optimizeGather2(ct, recs, i);
|
||||
optimizeGather1(ct, state, i);
|
||||
optimizeGather2(ct, state, i);
|
||||
}
|
||||
currec = findValidRule(ct, recs);
|
||||
currec = findValidRule(ct, state);
|
||||
if (currec != null) {
|
||||
applyOptimization(ct, currec);
|
||||
}
|
||||
}
|
||||
while (currec != null);
|
||||
checkUnusedTemps(ct, recs);
|
||||
checkUnusedTemps(ct, state);
|
||||
}
|
||||
|
||||
public ConsistencyChecker(SleighCompile cp, SubtableSymbol rt, boolean unnecessary,
|
||||
|
@ -1319,8 +1403,9 @@ class ConsistencyChecker {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the number of constructors which reference a varnode in the
|
||||
* unique space with size larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
|
||||
* Returns the number of constructors which reference a varnode in the unique space with size
|
||||
* larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
|
||||
*
|
||||
* @return num constructors with large temp varnodes
|
||||
*/
|
||||
public int getNumLargeTemporaries() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,6 +16,8 @@
|
|||
package ghidra.pcodeCPort.slgh_compile;
|
||||
|
||||
class OptimizeRecord {
|
||||
final long offset;
|
||||
final int size;
|
||||
int writeop;
|
||||
int readop;
|
||||
int inslot;
|
||||
|
@ -26,7 +27,10 @@ class OptimizeRecord {
|
|||
int readsection;
|
||||
int opttype;
|
||||
|
||||
OptimizeRecord() {
|
||||
OptimizeRecord(long offset, int size) {
|
||||
this.offset = offset;
|
||||
this.size = size;
|
||||
|
||||
writeop = -1;
|
||||
readop = -1;
|
||||
inslot = -1;
|
||||
|
@ -37,6 +41,55 @@ class OptimizeRecord {
|
|||
opttype = -1;
|
||||
}
|
||||
|
||||
public void copyFromExcludingSize(OptimizeRecord that) {
|
||||
this.writeop = that.writeop;
|
||||
this.readop = that.readop;
|
||||
this.inslot = that.inslot;
|
||||
this.writecount = that.writecount;
|
||||
this.readcount = that.readcount;
|
||||
this.writesection = that.writesection;
|
||||
this.readsection = that.readsection;
|
||||
this.opttype = that.opttype;
|
||||
}
|
||||
|
||||
public void updateRead(int i, int inslot, int secNum) {
|
||||
assert inslot >= 0;
|
||||
this.readop = i;
|
||||
this.readcount += 1;
|
||||
this.inslot = inslot;
|
||||
this.readsection = secNum;
|
||||
}
|
||||
|
||||
public void updateWrite(int i, int secNum) {
|
||||
this.writeop = i;
|
||||
this.writecount += 1;
|
||||
this.writesection = secNum;
|
||||
}
|
||||
|
||||
public void updateExport() {
|
||||
this.writeop = 0;
|
||||
this.readop = 0;
|
||||
this.writecount = 2;
|
||||
this.readcount = 2;
|
||||
this.readsection = -2;
|
||||
this.writesection = -2;
|
||||
}
|
||||
|
||||
public void updateCombine(OptimizeRecord that) {
|
||||
if (that.writecount != 0) {
|
||||
this.writeop = that.writeop;
|
||||
this.writesection = that.writesection;
|
||||
}
|
||||
if (that.readcount != 0) {
|
||||
this.readop = that.readop;
|
||||
this.inslot = that.inslot;
|
||||
this.readsection = that.readsection;
|
||||
}
|
||||
this.writecount += that.writecount;
|
||||
this.readcount += that.readcount;
|
||||
// opttype is not relevant here
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
|
|
@ -470,10 +470,6 @@ public abstract class PcodeCompile {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (basevn.getSpace().isUniqueSpace()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const_type offset_type = basevn.getOffset().getType();
|
||||
if ((offset_type != const_type.real) && (offset_type != const_type.handle)) {
|
||||
return null;
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.io.IOException;
|
|||
import ghidra.program.model.pcode.Encoder;
|
||||
import ghidra.sleigh.grammar.Location;
|
||||
|
||||
public class SleighSymbol implements Comparable<SleighSymbol> {
|
||||
public class SleighSymbol {
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
|
@ -85,11 +85,6 @@ public class SleighSymbol implements Comparable<SleighSymbol> {
|
|||
encodeSleighSymbolHeader(encoder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(SleighSymbol o) {
|
||||
return id - o.id;
|
||||
}
|
||||
|
||||
public final Location location;
|
||||
|
||||
public Location getLocation() {
|
||||
|
|
|
@ -17,42 +17,47 @@ package ghidra.program.model.pcode;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.pcodeCPort.opcodes.OpCode;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
|
||||
/**
|
||||
* An interface for writing structured data to a stream
|
||||
*
|
||||
* The resulting encoded data is structured similarly to an XML document. The document contains a nested set
|
||||
* of \elements, with labels corresponding to the ElementId class. A single element can hold
|
||||
* zero or more attributes and zero or more child elements. An attribute holds a primitive
|
||||
* data element (boolean, long, String) and is labeled by an AttributeId. The document is written
|
||||
* using a sequence of openElement() and closeElement() calls, intermixed with write*() calls to encode
|
||||
* the data primitives. All primitives written using a write*() call are associated with current open element,
|
||||
* and all write*() calls for one element must come before opening any child element.
|
||||
* The traditional XML element text content can be written using the special ATTRIB_CONTENT AttributeId, which
|
||||
* must be the last write*() call associated with the specific element.
|
||||
* <p>
|
||||
* The resulting encoded data is structured similarly to an XML document. The document contains a
|
||||
* nested set of <em>elements</em>, with labels corresponding to the ElementId class. A single
|
||||
* element can hold zero or more attributes and zero or more child elements. An attribute holds a
|
||||
* primitive data element (boolean, long, String) and is labeled by an AttributeId. The document is
|
||||
* written using a sequence of openElement() and closeElement() calls, intermixed with write*()
|
||||
* calls to encode the data primitives. All primitives written using a write*() call are associated
|
||||
* with current open element, and all write*() calls for one element must come before opening any
|
||||
* child element. The traditional XML element text content can be written using the special
|
||||
* ATTRIB_CONTENT AttributeId, which must be the last write*() call associated with the specific
|
||||
* element.
|
||||
*/
|
||||
public interface Encoder {
|
||||
|
||||
/**
|
||||
* Begin a new element in the encoding
|
||||
* The element will have the given ElementId annotation and becomes the \e current element.
|
||||
* Begin a new element in the encoding The element will have the given ElementId annotation and
|
||||
* becomes the \e current element.
|
||||
*
|
||||
* @param elemId is the given ElementId annotation
|
||||
* @throws IOException for errors in the underlying stream
|
||||
*/
|
||||
void openElement(ElementId elemId) throws IOException;
|
||||
|
||||
/**
|
||||
* End the current element in the encoding
|
||||
* The current element must match the given annotation or an exception is thrown.
|
||||
* End the current element in the encoding The current element must match the given annotation
|
||||
* or an exception is thrown.
|
||||
*
|
||||
* @param elemId is the given (expected) annotation for the current element
|
||||
* @throws IOException for errors in the underlying stream
|
||||
*/
|
||||
void closeElement(ElementId elemId) throws IOException;
|
||||
|
||||
/**
|
||||
* Write an annotated boolean value into the encoding
|
||||
* The boolean data is associated with the given AttributeId annotation and the current open element.
|
||||
* Write an annotated boolean value into the encoding The boolean data is associated with the
|
||||
* given AttributeId annotation and the current open element.
|
||||
*
|
||||
* @param attribId is the given AttributeId annotation
|
||||
* @param val is boolean value to encode
|
||||
* @throws IOException for errors in the underlying stream
|
||||
|
@ -60,8 +65,9 @@ public interface Encoder {
|
|||
void writeBool(AttributeId attribId, boolean val) throws IOException;
|
||||
|
||||
/**
|
||||
* Write an annotated signed integer value into the encoding
|
||||
* The integer is associated with the given AttributeId annotation and the current open element.
|
||||
* Write an annotated signed integer value into the encoding The integer is associated with the
|
||||
* given AttributeId annotation and the current open element.
|
||||
*
|
||||
* @param attribId is the given AttributeId annotation
|
||||
* @param val is the signed integer value to encode
|
||||
* @throws IOException for errors in the underlying stream
|
||||
|
@ -69,8 +75,9 @@ public interface Encoder {
|
|||
void writeSignedInteger(AttributeId attribId, long val) throws IOException;
|
||||
|
||||
/**
|
||||
* Write an annotated unsigned integer value into the encoding
|
||||
* The integer is associated with the given AttributeId annotation and the current open element.
|
||||
* Write an annotated unsigned integer value into the encoding The integer is associated with
|
||||
* the given AttributeId annotation and the current open element.
|
||||
*
|
||||
* @param attribId is the given AttributeId annotation
|
||||
* @param val is the unsigned integer value to encode
|
||||
* @throws IOException for errors in the underlying stream
|
||||
|
@ -78,8 +85,9 @@ public interface Encoder {
|
|||
void writeUnsignedInteger(AttributeId attribId, long val) throws IOException;
|
||||
|
||||
/**
|
||||
* Write an annotated string into the encoding
|
||||
* The string is associated with the given AttributeId annotation and the current open element.
|
||||
* Write an annotated string into the encoding The string is associated with the given
|
||||
* AttributeId annotation and the current open element.
|
||||
*
|
||||
* @param attribId is the given AttributeId annotation
|
||||
* @param val is the string to encode
|
||||
* @throws IOException for errors in the underlying stream
|
||||
|
@ -87,11 +95,12 @@ public interface Encoder {
|
|||
void writeString(AttributeId attribId, String val) throws IOException;
|
||||
|
||||
/**
|
||||
* Write an annotated string, using an indexed attribute, into the encoding.
|
||||
* Multiple attributes with a shared name can be written to the same element by calling this
|
||||
* method multiple times with a different index value. The encoding will use attribute ids up
|
||||
* to the base id plus the maximum index passed in. Implementors must be careful to not use
|
||||
* other attributes with ids bigger than the base id within the element taking the indexed attribute.
|
||||
* Write an annotated string, using an indexed attribute, into the encoding. Multiple attributes
|
||||
* with a shared name can be written to the same element by calling this method multiple times
|
||||
* with a different index value. The encoding will use attribute ids up to the base id plus the
|
||||
* maximum index passed in. Implementors must be careful to not use other attributes with ids
|
||||
* bigger than the base id within the element taking the indexed attribute.
|
||||
*
|
||||
* @param attribId is the shared AttributeId
|
||||
* @param index is the unique index to associated with the string
|
||||
* @param val is the string to encode
|
||||
|
@ -100,8 +109,9 @@ public interface Encoder {
|
|||
void writeStringIndexed(AttributeId attribId, int index, String val) throws IOException;
|
||||
|
||||
/**
|
||||
* Write an address space reference into the encoding
|
||||
* The address space is associated with the given AttributeId annotation and the current open element.
|
||||
* Write an address space reference into the encoding The address space is associated with the
|
||||
* given AttributeId annotation and the current open element.
|
||||
*
|
||||
* @param attribId is the given AttributeId annotation
|
||||
* @param spc is the address space to encode
|
||||
* @throws IOException for errors in the underlying stream
|
||||
|
@ -109,9 +119,9 @@ public interface Encoder {
|
|||
void writeSpace(AttributeId attribId, AddressSpace spc) throws IOException;
|
||||
|
||||
/**
|
||||
* Write an address space reference into the encoding.
|
||||
* An address space identified by its name and unique index is associated with the given
|
||||
* annotation and the current open element.
|
||||
* Write an address space reference into the encoding. An address space identified by its name
|
||||
* and unique index is associated with the given annotation and the current open element.
|
||||
*
|
||||
* @param attribId is the given annotation
|
||||
* @param index is the unique index of the address space
|
||||
* @param name is the name of the address space
|
||||
|
@ -120,11 +130,12 @@ public interface Encoder {
|
|||
void writeSpace(AttributeId attribId, int index, String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Write a p-code operation opcode into the encoding, associating it with the given
|
||||
* annotation. The opcode is specified based on the constants defined in {@link PcodeOp}.
|
||||
* Write a p-code operation opcode into the encoding, associating it with the given annotation.
|
||||
* The opcode is specified based on the constants defined in {@link PcodeOp}.
|
||||
*
|
||||
* @param attribId is the given annotation
|
||||
* @param opcode is the opcode constant
|
||||
* @throws IOException for errors in the underlying stream
|
||||
*/
|
||||
void writeOpcode(AttributeId attribId, int opcode) throws IOException;
|
||||
void writeOpcode(AttributeId attribId, OpCode opcode) throws IOException;
|
||||
}
|
||||
|
|
|
@ -20,11 +20,12 @@ import static ghidra.program.model.pcode.PackedDecode.*;
|
|||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import ghidra.pcodeCPort.opcodes.OpCode;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
|
||||
/**
|
||||
* A byte-based encoder designed to marshal to the decompiler efficiently
|
||||
* See {@code PackedDecode} for details of the encoding format
|
||||
* A byte-based encoder designed to marshal to the decompiler efficiently See {@code PackedDecode}
|
||||
* for details of the encoding format
|
||||
*/
|
||||
public class PackedEncode implements Encoder {
|
||||
protected OutputStream outStream;
|
||||
|
@ -201,9 +202,9 @@ public class PackedEncode implements Encoder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeOpcode(AttributeId attribId, int opcode) throws IOException {
|
||||
public void writeOpcode(AttributeId attribId, OpCode opcode) throws IOException {
|
||||
writeHeader(ATTRIBUTE, attribId.id());
|
||||
writeInteger((TYPECODE_SIGNEDINT_POSITIVE << TYPECODE_SHIFT), opcode);
|
||||
writeInteger((TYPECODE_SIGNEDINT_POSITIVE << TYPECODE_SHIFT), opcode.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,19 +15,19 @@
|
|||
*/
|
||||
package ghidra.program.model.pcode;
|
||||
|
||||
import static ghidra.program.model.pcode.AttributeId.*;
|
||||
import static ghidra.program.model.pcode.AttributeId.ATTRIB_CONTENT;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import ghidra.pcodeCPort.opcodes.OpCode;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.util.xml.SpecXmlUtils;
|
||||
|
||||
/**
|
||||
* An XML based encoder
|
||||
* The underlying transfer encoding is an XML document.
|
||||
* The encoder is initialized with a StringBuilder which will receive the XML document as calls
|
||||
* are made on the encoder.
|
||||
* An XML based encoder The underlying transfer encoding is an XML document. The encoder is
|
||||
* initialized with a StringBuilder which will receive the XML document as calls are made on the
|
||||
* encoder.
|
||||
*/
|
||||
public class XmlEncode implements CachedEncoder {
|
||||
|
||||
|
@ -233,8 +233,8 @@ public class XmlEncode implements CachedEncoder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeOpcode(AttributeId attribId, int opcode) throws IOException {
|
||||
String name = PcodeOp.getMnemonic(opcode);
|
||||
public void writeOpcode(AttributeId attribId, OpCode opcode) throws IOException {
|
||||
String name = opcode.getName();
|
||||
if (attribId == ATTRIB_CONTENT) {
|
||||
if (tagStatus == TAG_START) {
|
||||
buffer.append('>');
|
||||
|
|
|
@ -952,7 +952,7 @@ cc: "E" is cond=15 { tmp:1 = ($(Z) == 1); export tmp; }
|
|||
:POPF is op8=0xf3 {
|
||||
local result:2 = 0;
|
||||
pop(result);
|
||||
PSW = result:1;
|
||||
PSW = zext(result:1);
|
||||
local resultHi = result >> 8;
|
||||
INT_MASK = resultHi:1;
|
||||
}
|
||||
|
|
|
@ -3456,8 +3456,8 @@ define pcodeop contextswap;
|
|||
local div:2 = sext(TOK_10_7_Wreg) s/ sext(TOK_3_0_Wreg);
|
||||
local rem:2 = sext(TOK_10_7_Wreg) s% sext(TOK_3_0_Wreg);
|
||||
|
||||
W0 = div:1;
|
||||
W1 = rem:1;
|
||||
W0 = zext(div:1);
|
||||
W1 = zext(rem:1);
|
||||
|
||||
testSRL_N ( W1 );
|
||||
|
||||
|
@ -3545,8 +3545,8 @@ define pcodeop isDivideOverflow;
|
|||
local div:2 = zext(TOK_10_7_Wreg) / zext(TOK_3_0_Wreg);
|
||||
local rem:2 = zext(TOK_10_7_Wreg) % zext(TOK_3_0_Wreg);
|
||||
|
||||
W0 = div:1;
|
||||
W1 = rem:1;
|
||||
W0 = zext(div:1);
|
||||
W1 = zext(rem:1);
|
||||
|
||||
testSRL_N ( W1 );
|
||||
|
||||
|
@ -7204,13 +7204,13 @@ define pcodeop pwrsavOp;
|
|||
:sac.d ACCA_t^r4_t,Wsnd_t is OP_23_16=0xDC & OP_14=0x0 & OP_7_4=0x0 & ACCA_t & r4_t & Wsnd_t {
|
||||
|
||||
local tmp:6 = ACCA s>> (16 + r4_t);
|
||||
Wsnd_t = tmp:2;
|
||||
Wsnd_t = tmp:4;
|
||||
}
|
||||
|
||||
:sac.d ACCB_t^r4_t,Wsnd_t is OP_23_16=0xDC & OP_14=0x0 & OP_7_4=0x0 & ACCB_t & r4_t & Wsnd_t {
|
||||
|
||||
local tmp:6 = ACCB s>> (16 + r4_t);
|
||||
Wsnd_t = tmp:2;
|
||||
Wsnd_t = tmp:4;
|
||||
}
|
||||
@endif
|
||||
|
||||
|
|
|
@ -2088,9 +2088,9 @@ define pcodeop VectorMultiplyWordHighSignedSaturateFractionalToAccumulator2;
|
|||
# RT.h = temp.l;
|
||||
|
||||
lo:$(REGISTER_SIZE) = (( A & (0x0000000000000000) ) >> 32) * (( B & (0x0000000000000000) ) >> 32);
|
||||
lo = lo:4;
|
||||
lo = zext(lo:4);
|
||||
hi:$(REGISTER_SIZE) = (( A & (0xFFFFFFFF00000000) ) >> 32) * (( B & (0xFFFFFFFF00000000) ) >> 32);
|
||||
hi = hi:4;
|
||||
hi = zext(hi:4);
|
||||
D = (( zext(hi) << 32) | zext(lo) );
|
||||
}
|
||||
|
||||
|
@ -2106,9 +2106,9 @@ define pcodeop VectorMultiplyWordHighSignedSaturateFractionalToAccumulator2;
|
|||
# ACC = RT;
|
||||
|
||||
lo:$(REGISTER_SIZE) = (( A & (0x0000000000000000) ) >> 32) * (( B & (0x0000000000000000) ) >> 32);
|
||||
lo = lo:4;
|
||||
lo = zext(lo:4);
|
||||
hi:$(REGISTER_SIZE) = (( A & (0xFFFFFFFF00000000) ) >> 32) * (( B & (0xFFFFFFFF00000000) ) >> 32);
|
||||
hi = hi:4;
|
||||
hi = zext(hi:4);
|
||||
D = (( zext(hi) << 32) | zext(lo) );
|
||||
ACC = D;
|
||||
}
|
||||
|
|
|
@ -1764,7 +1764,8 @@ define pcodeop altv300_71;
|
|||
{
|
||||
local offs:2 = (12 - zext(A[0,4])) * 8;
|
||||
local out:16 = (vrB >> offs) & 0xffffffff;
|
||||
D = out:4;
|
||||
# No need for zext, as mask is already applied
|
||||
D = out:$(REGISTER_SIZE);
|
||||
}
|
||||
|
||||
:vextuwrx D,A,vrB is OP=4 & D & A & vrB & XOP_0_10=1933 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue