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