Merge remote-tracking branch 'origin/GP-5581_sleighPiecewiseUniques--SQUASHED'

This commit is contained in:
Ryan Kurtz 2025-08-18 13:44:28 -04:00
commit 6773801f6e
20 changed files with 798 additions and 482 deletions

View file

@ -583,9 +583,6 @@ VarnodeTpl *PcodeCompile::buildTruncatedVarnode(VarnodeTpl *basevn,uint4 bitoffs
if ((bitoffset % 8) != 0) return (VarnodeTpl *)0;
if ((numbits % 8) != 0) return (VarnodeTpl *)0;
if (basevn->getSpace().isUniqueSpace()) // Do we really want to prevent truncated uniques??
return (VarnodeTpl *)0;
ConstTpl::const_type offset_type = basevn->getOffset().getType();
if ((offset_type != ConstTpl::real)&&(offset_type != ConstTpl::handle))
return (VarnodeTpl *)0;

View file

@ -540,6 +540,18 @@ void VarnodeTpl::decode(Decoder &decoder)
decoder.closeElement(el);
}
bool VarnodeTpl::operator==(const VarnodeTpl &op2) const
{
return space==op2.space && offset==op2.offset && size==op2.size;
}
bool VarnodeTpl::operator!=(const VarnodeTpl &op2) const
{
return !(*this == op2);
}
bool VarnodeTpl::operator<(const VarnodeTpl &op2) const
{

View file

@ -91,6 +91,8 @@ public:
bool isDynamic(const ParserWalker &walker) const;
int4 transfer(const vector<HandleTpl *> &params);
bool isZeroSize(void) const { return size.isZero(); }
bool operator==(const VarnodeTpl &op2) const;
bool operator!=(const VarnodeTpl &op2) const;
bool operator<(const VarnodeTpl &op2) const;
void setOffset(uintb constVal) { offset = ConstTpl(ConstTpl::real,constVal); }
void setRelative(uintb constVal) { offset = ConstTpl(ConstTpl::j_relative,constVal); }

View file

@ -180,6 +180,75 @@ SubtableSymbol *WithBlock::getCurrentSubtable(const list<WithBlock> &stack)
return (SubtableSymbol *)0;
}
void ConsistencyChecker::OptimizeRecord::copyFromExcludingSize(ConsistencyChecker::OptimizeRecord &that)
{
this->writeop = that.writeop;
this->readop = that.readop;
this->inslot = that.inslot;
this->writecount = that.writecount;
this->readcount = that.readcount;
this->writesection = that.writesection;
this->readsection = that.readsection;
this->opttype = that.opttype;
}
void ConsistencyChecker::OptimizeRecord::update(int4 opIdx, int4 slotIdx, int4 secNum)
{
if (slotIdx >= 0) {
updateRead(opIdx, slotIdx, secNum);
}
else {
updateWrite(opIdx, secNum);
}
}
void ConsistencyChecker::OptimizeRecord::updateRead(int4 i, int4 inslot, int4 secNum)
{
this->readop = i;
this->readcount++;
this->inslot = inslot;
this->readsection = secNum;
}
void ConsistencyChecker::OptimizeRecord::updateWrite(int4 i, int4 secNum)
{
this->writeop = i;
this->writecount++;
this->writesection = secNum;
}
void ConsistencyChecker::OptimizeRecord::updateExport()
{
this->writeop = 0;
this->readop = 0;
this->writecount = 2;
this->readcount = 2;
this->readsection = -2;
this->writesection = -2;
}
void ConsistencyChecker::OptimizeRecord::updateCombine(ConsistencyChecker::OptimizeRecord &that)
{
if (that.writecount != 0) {
this->writeop = that.writeop;
this->writesection = that.writesection;
}
if (that.readcount != 0) {
this->readop = that.readop;
this->inslot = that.inslot;
this->readsection = that.readsection;
}
this->writecount += that.writecount;
this->readcount += that.readcount;
// opttype is not relevant here
}
/// \brief Construct the consistency checker and optimizer
///
/// \param sleigh is the parsed SLEIGH spec
@ -1129,6 +1198,90 @@ void ConsistencyChecker::setPostOrder(SubtableSymbol *root)
}
}
map<uintb,ConsistencyChecker::OptimizeRecord>::iterator ConsistencyChecker::UniqueState::lesserIter(uintb offset)
{
if (recs.begin() == recs.end()) {
return recs.end();
}
map<uintb,OptimizeRecord>::iterator iter;
iter = recs.lower_bound(offset);
if (iter == recs.begin()) {
return recs.end();
}
return std::prev(iter);
}
ConsistencyChecker::OptimizeRecord ConsistencyChecker::UniqueState::coalesce(vector<ConsistencyChecker::OptimizeRecord*> &records)
{
uintb minOff = -1;
uintb maxOff = -1;
vector<OptimizeRecord*>::iterator iter;
for (iter = records.begin(); iter != records.end(); ++iter) {
if (minOff == -1 || (*iter)->offset < minOff) {
minOff = (*iter)->offset;
}
if (maxOff == -1 || (*iter)->offset + (*iter)->size > maxOff) {
maxOff = (*iter)->offset + (*iter)->size;
}
}
OptimizeRecord result(minOff, maxOff - minOff);
for (iter = records.begin(); iter != records.end(); ++iter) {
result.updateCombine(**iter);
}
return result;
}
void ConsistencyChecker::UniqueState::set(uintb offset, int4 size, OptimizeRecord &rec)
{
vector<OptimizeRecord*> records;
getDefinitions(records, offset, size);
records.push_back(&rec);
OptimizeRecord coalesced = coalesce(records);
recs.erase(recs.lower_bound(coalesced.offset), recs.lower_bound(coalesced.offset+coalesced.size));
recs.insert(pair<uint4,OptimizeRecord>(coalesced.offset, coalesced));
}
void ConsistencyChecker::UniqueState::getDefinitions(vector<ConsistencyChecker::OptimizeRecord*> &result, uintb offset, int4 size)
{
if (size == 0) {
size = 1;
}
map<uintb,OptimizeRecord>::iterator iter;
iter = lesserIter(offset);
uintb cursor = offset;
if (iter != recs.end() && endOf(iter) > offset) {
OptimizeRecord &preRec = iter->second;
cursor = endOf(iter);
result.push_back(&preRec);
}
uintb end = offset + size;
iter = recs.lower_bound(offset);
while (iter != recs.end() && iter->first < end) {
if (iter->first > cursor) {
// The iterator becomes invalid with this insertion, so take the new one.
iter = recs.insert(pair<uint4,OptimizeRecord>(cursor,OptimizeRecord(cursor, iter->first - cursor))).first;
result.push_back(&iter->second);
iter++; // Put the (now valid) iterator back to where it was.
}
// No need to truncate, as we're just counting a read
result.push_back(&iter->second);
cursor = endOf(iter);
iter++;
}
if (end > cursor) {
iter = recs.insert(pair<uint4,OptimizeRecord>(cursor,OptimizeRecord(cursor, end - cursor))).first;
result.push_back(&iter->second);
}
}
/// \brief Test whether two given Varnodes intersect
///
/// This test must be conservative. If it can't explicitly prove that the
@ -1222,30 +1375,31 @@ bool ConsistencyChecker::readWriteInterference(const VarnodeTpl *vn,const OpTpl
/// If the Varnode is in the \e unique space, an OptimizationRecord for it is looked
/// up based on its offset. Information about how a p-code operator uses the Varnode
/// is accumulated in the record.
/// \param recs is collection of OptimizationRecords associated with temporary Varnodes
/// \param state is collection of OptimizationRecords associated with temporary Varnodes
/// \param vn is the given Varnode to check (which may or may not be temporary)
/// \param i is the index of the operator using the Varnode (within its p-code section)
/// \param inslot is the \e slot index of the Varnode within its operator
/// \param secnum is the section number containing the operator
void ConsistencyChecker::examineVn(map<uintb,OptimizeRecord> &recs,
void ConsistencyChecker::examineVn(UniqueState &state,
const VarnodeTpl *vn,uint4 i,int4 inslot,int4 secnum)
{
if (vn == (const VarnodeTpl *)0) return;
if (!vn->getSpace().isUniqueSpace()) return;
if (vn->getOffset().getType() != ConstTpl::real) return;
map<uintb,OptimizeRecord>::iterator iter;
iter = recs.insert( pair<uint4,OptimizeRecord>(vn->getOffset().getReal(),OptimizeRecord())).first;
uintb offset = vn->getOffset().getReal();
int4 size = vn->getSize().getReal();
if (inslot >= 0) {
(*iter).second.readop = i;
(*iter).second.readcount += 1;
(*iter).second.inslot = inslot;
(*iter).second.readsection = secnum;
vector<OptimizeRecord*> defs;
state.getDefinitions(defs,offset,size);
for (vector<OptimizeRecord*>::iterator iter=defs.begin();iter!=defs.end();++iter) {
(*iter)->updateRead(i,inslot,secnum);
}
}
else {
(*iter).second.writeop = i;
(*iter).second.writecount += 1;
(*iter).second.writesection = secnum;
OptimizeRecord rec(offset,size);
rec.updateWrite(i,secnum);
state.set(offset,size,rec);
}
}
@ -1254,9 +1408,9 @@ void ConsistencyChecker::examineVn(map<uintb,OptimizeRecord> &recs,
/// For each temporary Varnode, count how many times it is read from or written to
/// in the given section of p-code operators.
/// \param ct is the given Constructor
/// \param recs is the (initially empty) collection of count records
/// \param state is the (initially empty) collection of count records
/// \param secnum is the given p-code section number
void ConsistencyChecker::optimizeGather1(Constructor *ct,map<uintb,OptimizeRecord> &recs,int4 secnum) const
void ConsistencyChecker::optimizeGather1(Constructor *ct,UniqueState &state,int4 secnum) const
{
ConstructTpl *tpl;
@ -1271,10 +1425,10 @@ void ConsistencyChecker::optimizeGather1(Constructor *ct,map<uintb,OptimizeRecor
const OpTpl *op = ops[i];
for(uint4 j=0;j<op->numInput();++j) {
const VarnodeTpl *vnin = op->getIn(j);
examineVn(recs,vnin,i,j,secnum);
examineVn(state,vnin,i,j,secnum);
}
const VarnodeTpl *vn = op->getOut();
examineVn(recs,vn,i,-1,secnum);
examineVn(state,vn,i,-1,secnum);
}
}
@ -1284,9 +1438,9 @@ void ConsistencyChecker::optimizeGather1(Constructor *ct,map<uintb,OptimizeRecor
/// for the section, and if it involves a temporary, mark it as both read and written, guaranteeing
/// that the Varnode is not optimized away.
/// \param ct is the given Constructor
/// \param recs is the collection of count records
/// \param state is the collection of count records
/// \param secnum is the given p-code section number
void ConsistencyChecker::optimizeGather2(Constructor *ct,map<uintb,OptimizeRecord> &recs,int4 secnum) const
void ConsistencyChecker::optimizeGather2(Constructor *ct,UniqueState &state,int4 secnum) const
{
ConstructTpl *tpl;
@ -1300,29 +1454,29 @@ void ConsistencyChecker::optimizeGather2(Constructor *ct,map<uintb,OptimizeRecor
if (hand == (HandleTpl *)0) return;
if (hand->getPtrSpace().isUniqueSpace()) {
if (hand->getPtrOffset().getType() == ConstTpl::real) {
pair<map<uintb,OptimizeRecord>::iterator,bool> res;
uintb offset = hand->getPtrOffset().getReal();
res = recs.insert( pair<uintb,OptimizeRecord>(offset,OptimizeRecord()));
(*res.first).second.writeop = 0;
(*res.first).second.readop = 0;
(*res.first).second.writecount = 2;
(*res.first).second.readcount = 2;
(*res.first).second.readsection = -2;
(*res.first).second.writesection = -2;
int4 size = hand->getPtrSize().getReal();
vector<OptimizeRecord*> defs;
state.getDefinitions(defs,offset,size);
for (vector<OptimizeRecord*>::iterator iter=defs.begin();iter!=defs.end();++iter) {
(*iter)->updateExport();
// NOTE: Could this just be updateRead?
// Technically, an exported handle could be written by the parent....
}
}
}
if (hand->getSpace().isUniqueSpace()) {
if ((hand->getPtrSpace().getType() == ConstTpl::real)&&
(hand->getPtrOffset().getType() == ConstTpl::real)) {
pair<map<uintb,OptimizeRecord>::iterator,bool> res;
uintb offset = hand->getPtrOffset().getReal();
res = recs.insert( pair<uintb,OptimizeRecord>(offset,OptimizeRecord()));
(*res.first).second.writeop = 0;
(*res.first).second.readop = 0;
(*res.first).second.writecount = 2;
(*res.first).second.readcount = 2;
(*res.first).second.readsection = -2;
(*res.first).second.writesection = -2;
int4 size = hand->getPtrSize().getReal();
vector<OptimizeRecord*> defs;
state.getDefinitions(defs,offset,size);
for (vector<OptimizeRecord*>::iterator iter=defs.begin();iter!=defs.end();++iter) {
(*iter)->updateExport();
// NOTE: Could this just be updateRead?
// Technically, an exported handle could be written by the parent....
}
}
}
}
@ -1336,14 +1490,14 @@ void ConsistencyChecker::optimizeGather2(Constructor *ct,map<uintb,OptimizeRecor
/// if propagation is forward, the Varnode must not cross another write.
/// If all the requirements pass, return the record indicating that the COPY can be removed.
/// \param ct is the Constructor owning the p-code
/// \param recs is the collection of OptimizeRecords to search
/// \param state is the collection of OptimizeRecords to search
/// \return a passing OptimizeRecord or null
const ConsistencyChecker::OptimizeRecord *ConsistencyChecker::findValidRule(Constructor *ct,
const map<uintb,OptimizeRecord> &recs) const
const UniqueState &state) const
{
map<uintb,OptimizeRecord>::const_iterator iter;
iter = recs.begin();
while(iter != recs.end()) {
iter = state.begin();
while(iter!=state.end()) {
const OptimizeRecord &currec( (*iter).second );
++iter;
if ((currec.writecount==1)&&(currec.readcount==1)&&(currec.readsection==currec.writesection)) {
@ -1354,13 +1508,27 @@ const ConsistencyChecker::OptimizeRecord *ConsistencyChecker::findValidRule(Cons
else
tpl = ct->getNamedTempl(currec.readsection);
const vector<OpTpl *> &ops( tpl->getOpvec() );
const OpTpl *op = ops[ currec.readop ];
const OpTpl *writeop = ops[ currec.writeop ];
const OpTpl *readop = ops[ currec.readop ];
if (currec.writeop >= currec.readop) // Read must come after write
throw SleighError("Read of temporary before write");
if (op->getOpcode() == CPUI_COPY) {
VarnodeTpl *writevn = writeop->getOut();
VarnodeTpl *readvn = readop->getIn(currec.inslot);
// Because the record can change size and position, we have to check if the varnode
// "connecting" the write and read ops is actually the same varnode. If not, then we can't
// optimize it out.
// There may be an opportunity here to re-write the size/offset when either the write or read
// op is a COPY, but I'll leave that for later discussion.
// Actually, maybe not. If the truncate would be of a handle, we can't.
if (*writevn != *readvn) {
continue;
}
if (readop->getOpcode() == CPUI_COPY) {
bool saverecord = true;
currec.opttype = 0; // Read op is a COPY
const VarnodeTpl *vn = op->getOut();
const VarnodeTpl *vn = readop->getOut();
for(int4 i=currec.writeop+1;i<currec.readop;++i) { // Check for interference between write and read
if (readWriteInterference(vn,ops[i],true)) {
saverecord = false;
@ -1370,11 +1538,10 @@ const ConsistencyChecker::OptimizeRecord *ConsistencyChecker::findValidRule(Cons
if (saverecord)
return &currec;
}
op = ops[ currec.writeop ];
if (op->getOpcode() == CPUI_COPY) {
if (writeop->getOpcode() == CPUI_COPY) {
bool saverecord = true;
currec.opttype = 1; // Write op is a COPY
const VarnodeTpl *vn = op->getIn(0);
const VarnodeTpl *vn = writeop->getIn(0);
for(int4 i=currec.writeop+1;i<currec.readop;++i) { // Check for interference between write and read
if (readWriteInterference(vn,ops[i],false)) {
saverecord = false;
@ -1431,13 +1598,13 @@ void ConsistencyChecker::applyOptimization(Constructor *ct,const OptimizeRecord
/// An error message is issued if a temporary is read but not written.
/// A warning may be issued if a temporary is written but not read.
/// \param ct is the Constructor
/// \param recs is the collection of records associated with each temporary Varnode
void ConsistencyChecker::checkUnusedTemps(Constructor *ct,const map<uintb,OptimizeRecord> &recs)
/// \param state is the collection of records associated with each temporary Varnode
void ConsistencyChecker::checkUnusedTemps(Constructor *ct,const UniqueState &state)
{
map<uintb,OptimizeRecord>::const_iterator iter;
iter = recs.begin();
while(iter != recs.end()) {
iter = state.begin();
while(iter != state.end()) {
const OptimizeRecord &currec( (*iter).second );
if (currec.readcount == 0) {
if (printdeadwarning)
@ -1485,19 +1652,19 @@ void ConsistencyChecker::optimize(Constructor *ct)
{
const OptimizeRecord *currec;
map<uintb,OptimizeRecord> recs;
UniqueState state;
int4 numsections = ct->getNumSections();
do {
recs.clear();
state.clear();
for(int4 i=-1;i<numsections;++i) {
optimizeGather1(ct,recs,i);
optimizeGather2(ct,recs,i);
optimizeGather1(ct,state,i);
optimizeGather2(ct,state,i);
}
currec = findValidRule(ct,recs);
currec = findValidRule(ct,state);
if (currec != (const OptimizeRecord *)0)
applyOptimization(ct,*currec);
} while(currec != (const OptimizeRecord *)0);
checkUnusedTemps(ct,recs);
checkUnusedTemps(ct,state);
}
/// Warnings or errors for individual violations may be printed, depending on settings.
@ -1589,6 +1756,13 @@ void ConsistencyChecker::optimizeAll(void)
}
}
ostream& operator<<(ostream &os, const ConsistencyChecker::OptimizeRecord &rec) {
os << "{writeop=" << rec.writeop << " readop=" << rec.readop << " inslot=" << rec.inslot <<
" writecount=" << rec.writecount << " readcount=" << rec.readcount <<
" opttype=" << rec.opttype << "}";
return os;
}
/// Sort based on the containing Varnode, then on the bit boundary
/// \param op2 is a field to compare with \b this
/// \return \b true if \b this should be sorted before the other field

View file

@ -23,12 +23,14 @@
#include "filemanage.hh"
#include <iostream>
#include <sstream>
#include <string>
namespace ghidra {
using std::cout;
using std::cerr;
using std::out_of_range;
using std::string;
/// \brief A helper class to associate a \e named Constructor section with its symbol scope
///
@ -137,13 +139,15 @@ class SleighCompile;
/// This class searches for unnecessary truncations and extensions, temporary varnodes that are either dead,
/// read before written, or that exceed the standard allocation size.
class ConsistencyChecker {
public:
/// \brief Description of how a temporary register is being used within a Constructor
///
/// This counts reads and writes of the register. If the register is read only once, the
/// particular p-code op and input slot reading it is recorded. If the register is written
/// only once, the particular p-code op writing it is recorded.
struct OptimizeRecord {
uintb offset; ///< Offset of the varnode address
int4 size; ///< Size in bytes of the varnode or piece (immutable)
int4 writeop; ///< Index of the (last) p-code op writing to register (or -1)
int4 readop; ///< Index of the (last) p-code op reading the register (or -1)
int4 inslot; ///< Input slot of p-code op reading the register (or -1)
@ -154,9 +158,32 @@ class ConsistencyChecker {
mutable int4 opttype; ///< 0 = register read by a COPY, 1 = register written by a COPY (-1 otherwise)
/// \brief Construct a record, initializing counts
OptimizeRecord(void) {
writeop = -1; readop = -1; inslot=-1; writecount=0; readcount=0; writesection=-2; readsection=-2; opttype=-1; }
OptimizeRecord(uintb offset, int4 size) {
this->offset = offset;
this->size = size;
writeop = -1; readop = -1; inslot=-1; writecount=0; readcount=0; writesection=-2; readsection=-2; opttype=-1;
}
void copyFromExcludingSize(OptimizeRecord &that);
void update(int4 opIdx, int4 slotIdx, int4 secNum);
void updateRead(int4 i, int4 inslot, int4 secNum);
void updateWrite(int4 i, int4 secNum);
void updateExport();
void updateCombine(OptimizeRecord &that);
};
private:
class UniqueState {
map<uintb,OptimizeRecord> recs;
static uintb endOf(map<uintb,OptimizeRecord>::iterator &iter) { return iter->first + iter->second.size; }
OptimizeRecord coalesce(vector<OptimizeRecord*> &records);
map<uintb,OptimizeRecord>::iterator lesserIter(uintb offset);
public:
void clear(void) { recs.clear(); }
void set(uintb offset, int4 size, OptimizeRecord &rec);
void getDefinitions(vector<OptimizeRecord*> &result, uintb offset, int4 size);
map<uintb,OptimizeRecord>::const_iterator begin(void) const { return recs.begin(); }
map<uintb,OptimizeRecord>::const_iterator end(void) const { return recs.end(); }
};
SleighCompile *compiler; ///< Parsed form of the SLEIGH file being examined
int4 unnecessarypcode; ///< Count of unnecessary extension/truncation operations
int4 readnowrite; ///< Count of temporary registers that are read but not written
@ -185,14 +212,14 @@ class ConsistencyChecker {
void setPostOrder(SubtableSymbol *root);
// Optimization routines
static void examineVn(map<uintb,OptimizeRecord> &recs,const VarnodeTpl *vn,uint4 i,int4 inslot,int4 secnum);
static void examineVn(UniqueState &state,const VarnodeTpl *vn,uint4 i,int4 inslot,int4 secnum);
static bool possibleIntersection(const VarnodeTpl *vn1,const VarnodeTpl *vn2);
bool readWriteInterference(const VarnodeTpl *vn,const OpTpl *op,bool checkread) const;
void optimizeGather1(Constructor *ct,map<uintb,OptimizeRecord> &recs,int4 secnum) const;
void optimizeGather2(Constructor *ct,map<uintb,OptimizeRecord> &recs,int4 secnum) const;
const OptimizeRecord *findValidRule(Constructor *ct,const map<uintb,OptimizeRecord> &recs) const;
void optimizeGather1(Constructor *ct,UniqueState &state,int4 secnum) const;
void optimizeGather2(Constructor *ct,UniqueState &state,int4 secnum) const;
const OptimizeRecord *findValidRule(Constructor *ct,const UniqueState &state) const;
void applyOptimization(Constructor *ct,const OptimizeRecord &rec);
void checkUnusedTemps(Constructor *ct,const map<uintb,OptimizeRecord> &recs);
void checkUnusedTemps(Constructor *ct,const UniqueState &state);
void checkLargeTemporaries(Constructor *ct,ConstructTpl *ctpl);
void optimize(Constructor *ct);
public:
@ -458,6 +485,8 @@ public:
int4 run_compilation(const string &filein,const string &fileout);
};
ostream& operator<<(ostream &os, const ConsistencyChecker::OptimizeRecord &rec);
extern SleighCompile *slgh; ///< A global reference to the SLEIGH compiler accessible to the parse functions
extern int yydebug; ///< Debug state for the SLEIGH parse functions

View file

@ -16,6 +16,11 @@
package ghidra.pcodeCPort.opcodes;
// Names of ops associated with their opcode number
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// Some of the names have been replaced with special placeholder
// ops for the sleigh compiler and interpreter these are as follows:
// MULTIEQUAL = BUILD
@ -24,219 +29,168 @@ package ghidra.pcodeCPort.opcodes;
// PTRSUB = CROSSBUILD
public enum OpCode {
DO_NOT_USE_ME_I_AM_ENUM_ELEMENT_ZERO,
CPUI_COPY, // Copy one operand to another
CPUI_LOAD, // Dereference a pointer into specified space
CPUI_STORE, // Store at a pointer into specified space
DO_NOT_USE_ME_I_AM_ENUM_ELEMENT_ZERO("BLANK"),
CPUI_COPY("COPY"), // Copy one operand to another
CPUI_LOAD("LOAD"), // Dereference a pointer into specified space
CPUI_STORE("STORE"), // Store at a pointer into specified space
CPUI_BRANCH, // Always branch
CPUI_CBRANCH, // Conditional branch
CPUI_BRANCHIND, // An indirect branch (jumptable)
CPUI_BRANCH("BRANCH"), // Always branch
CPUI_CBRANCH("CBRANCH"), // Conditional branch
CPUI_BRANCHIND("BRANCHIND"), // An indirect branch (jumptable)
CPUI_CALL, // A call with absolute address
CPUI_CALLIND, // An indirect call
CPUI_CALLOTHER, // Other unusual subroutine calling conventions
CPUI_RETURN, // A return from subroutine
CPUI_CALL("CALL"), // A call with absolute address
CPUI_CALLIND("CALLIND"), // An indirect call
CPUI_CALLOTHER("CALLOTHER"), // Other unusual subroutine calling conventions
CPUI_RETURN("RETURN"), // A return from subroutine
// Integer/bit operations
CPUI_INT_EQUAL, // Return TRUE if operand1 == operand2
CPUI_INT_NOTEQUAL, // Return TRUE if operand1 != operand2
CPUI_INT_SLESS, // Return TRUE if signed op1 < signed op2
CPUI_INT_SLESSEQUAL, // Return TRUE if signed op1 <= signed op2
CPUI_INT_LESS, // Return TRUE if unsigned op1 < unsigned op2
CPUI_INT_EQUAL("INT_EQUAL"), // Return TRUE if operand1 == operand2
CPUI_INT_NOTEQUAL("INT_NOTEQUAL"), // Return TRUE if operand1 != operand2
CPUI_INT_SLESS("INT_SLESS"), // Return TRUE if signed op1 < signed op2
CPUI_INT_SLESSEQUAL("INT_SLESSEQUAL"), // Return TRUE if signed op1 <= signed op2
CPUI_INT_LESS("INT_LESS"), // Return TRUE if unsigned op1 < unsigned op2
// This also indicates a borrow on unsigned substraction
CPUI_INT_LESSEQUAL, // Return TRUE if unsigned op1 <= unsigned op2
CPUI_INT_ZEXT, // Zero extend operand
CPUI_INT_SEXT, // Sign extend operand
CPUI_INT_ADD, // Unsigned addition of operands of same size
CPUI_INT_SUB, // Unsigned subtraction of operands of same size
CPUI_INT_CARRY, // TRUE if adding two operands has overflow (carry)
CPUI_INT_SCARRY, // TRUE if there is a carry in signed addition of two ops
CPUI_INT_SBORROW, // TRUE if there is a borrow in signed subtraction of two ops
CPUI_INT_2COMP, // Twos complement (for subtracting) of operand
CPUI_INT_NEGATE,
CPUI_INT_XOR, // Exclusive OR of two operands of same size
CPUI_INT_AND,
CPUI_INT_OR,
CPUI_INT_LEFT, // Left shift
CPUI_INT_RIGHT, // Right shift zero fill
CPUI_INT_SRIGHT, // Signed right shift
CPUI_INT_MULT, // Integer multiplication
CPUI_INT_DIV, // Unsigned integer division
CPUI_INT_SDIV, // Signed integer division
CPUI_INT_REM, // Unsigned mod (remainder)
CPUI_INT_SREM, // Signed mod (remainder)
CPUI_INT_LESSEQUAL("INT_LESSEQUAL"), // Return TRUE if unsigned op1 <= unsigned op2
CPUI_INT_ZEXT("INT_ZEXT"), // Zero extend operand
CPUI_INT_SEXT("INT_SEXT"), // Sign extend operand
CPUI_INT_ADD("INT_ADD"), // Unsigned addition of operands of same size
CPUI_INT_SUB("INT_SUB"), // Unsigned subtraction of operands of same size
CPUI_INT_CARRY("INT_CARRY"), // TRUE if adding two operands has overflow (carry)
CPUI_INT_SCARRY("INT_SCARRY"), // TRUE if there is a carry in signed addition of two ops
CPUI_INT_SBORROW("INT_SBORROW"), // TRUE if there is a borrow in signed subtraction of two ops
CPUI_INT_2COMP("INT_2COMP"), // Twos complement (for subtracting) of operand
CPUI_INT_NEGATE("INT_NEGATE"),
CPUI_INT_XOR("INT_XOR"), // Exclusive OR of two operands of same size
CPUI_INT_AND("INT_AND"),
CPUI_INT_OR("INT_OR"),
CPUI_INT_LEFT("INT_LEFT"), // Left shift
CPUI_INT_RIGHT("INT_RIGHT"), // Right shift zero fill
CPUI_INT_SRIGHT("INT_SRIGHT"), // Signed right shift
CPUI_INT_MULT("INT_MULT"), // Integer multiplication
CPUI_INT_DIV("INT_DIV"), // Unsigned integer division
CPUI_INT_SDIV("INT_SDIV"), // Signed integer division
CPUI_INT_REM("INT_REM"), // Unsigned mod (remainder)
CPUI_INT_SREM("INT_SREM"), // Signed mod (remainder)
CPUI_BOOL_NEGATE, // Boolean negate or not
CPUI_BOOL_XOR, // Boolean xor
CPUI_BOOL_AND, // Boolean and (&&)
CPUI_BOOL_OR, // Boolean or (||)
CPUI_BOOL_NEGATE("BOOL_NEGATE"), // Boolean negate or not
CPUI_BOOL_XOR("BOOL_XOR"), // Boolean xor
CPUI_BOOL_AND("BOOL_AND"), // Boolean and (&&)
CPUI_BOOL_OR("BOOL_OR"), // Boolean or (||)
// Floating point operations
CPUI_FLOAT_EQUAL, // Return TRUE if operand1 == operand2
CPUI_FLOAT_NOTEQUAL, // Return TRUE if operand1 != operand2
CPUI_FLOAT_LESS, // Return TRUE if op1 < op2
CPUI_FLOAT_LESSEQUAL, // Return TRUE if op1 <= op2
CPUI_UNUSED1, // Slot 45 is unused
CPUI_FLOAT_NAN, // Return TRUE if op1 is NaN
CPUI_FLOAT_EQUAL("FLOAT_EQUAL"), // Return TRUE if operand1 == operand2
CPUI_FLOAT_NOTEQUAL("FLOAT_NOTEQUAL"), // Return TRUE if operand1 != operand2
CPUI_FLOAT_LESS("FLOAT_LESS"), // Return TRUE if op1 < op2
CPUI_FLOAT_LESSEQUAL("FLOAT_LESSEQUAL"), // Return TRUE if op1 <= op2
CPUI_UNUSED1("UNUSED1"), // Slot 45 is unused
CPUI_FLOAT_NAN("FLOAT_NAN"), // Return TRUE if op1 is NaN
CPUI_FLOAT_ADD, // float addition
CPUI_FLOAT_DIV, // float division
CPUI_FLOAT_MULT, // float multiplication
CPUI_FLOAT_SUB, // float subtraction
CPUI_FLOAT_NEG, // float negation
CPUI_FLOAT_ABS, // float absolute value
CPUI_FLOAT_SQRT, // float square root
CPUI_FLOAT_ADD("FLOAT_ADD"), // float addition
CPUI_FLOAT_DIV("FLOAT_DIV"), // float division
CPUI_FLOAT_MULT("FLOAT_MULT"), // float multiplication
CPUI_FLOAT_SUB("FLOAT_SUB"), // float subtraction
CPUI_FLOAT_NEG("FLOAT_NEG"), // float negation
CPUI_FLOAT_ABS("FLOAT_ABS"), // float absolute value
CPUI_FLOAT_SQRT("FLOAT_SQRT"), // float square root
CPUI_FLOAT_INT2FLOAT, // convert int type to float type
CPUI_FLOAT_FLOAT2FLOAT, // convert between float sizes
CPUI_FLOAT_TRUNC, // round towards zero
CPUI_FLOAT_CEIL, // round towards +infinity
CPUI_FLOAT_FLOOR, // round towards -infinity
CPUI_FLOAT_ROUND, // round towards nearest
CPUI_FLOAT_INT2FLOAT("INT2FLOAT"), // convert int type to float type
CPUI_FLOAT_FLOAT2FLOAT("FLOAT2FLOAT"), // convert between float sizes
CPUI_FLOAT_TRUNC("TRUNC"), // round towards zero
CPUI_FLOAT_CEIL("CEIL"), // round towards +infinity
CPUI_FLOAT_FLOOR("FLOOR"), // round towards -infinity
CPUI_FLOAT_ROUND("ROUND"), // round towards nearest
// Internal opcodes for simplification. Not
// typically generated in a direct translation.
// Dataflow operations
CPUI_MULTIEQUAL, // Output is equal to one of its inputs, depending on execution // BUILD
CPUI_INDIRECT, // Output probably equals input but may be indirectly affected // DELAY_SLOT
CPUI_PIECE, // Output is constructed from multiple pieces
CPUI_SUBPIECE, // Output is a subpiece of input0, input1=offset into input0
CPUI_MULTIEQUAL("BUILD"), // Output is equal to one of its inputs, depending on execution
CPUI_INDIRECT("DELAY_SLOT"), // Output probably equals input but may be indirectly affected
CPUI_PIECE("PIECE"), // Output is constructed from multiple pieces
CPUI_SUBPIECE("SUBPIECE"), // Output is a subpiece of input0, input1=offset into input0
CPUI_CAST, // Cast from one type to another // MACROBUILD
CPUI_PTRADD, // outptr = ptrbase, offset, (size multiplier) // LABELBUILD
CPUI_PTRSUB, // outptr = &(ptr->subfield) // CROSSBUILD
CPUI_SEGMENTOP,
CPUI_CPOOLREF,
CPUI_NEW,
CPUI_INSERT,
CPUI_EXTRACT,
CPUI_POPCOUNT,
CPUI_LZCOUNT,
CPUI_CAST("CAST"), // Cast from one type to another // MACROBUILD
CPUI_PTRADD("LABEL"), // outptr = ptrbase, offset, (size multiplier)
CPUI_PTRSUB("CROSSBUILD"), // outptr = &(ptr->subfield)
CPUI_SEGMENTOP("SEGMENTOP"),
CPUI_CPOOLREF("CPOOLREF"),
CPUI_NEW("NEW"),
CPUI_INSERT("INSERT"),
CPUI_EXTRACT("EXTRACT"),
CPUI_POPCOUNT("POPCOUNT"),
CPUI_LZCOUNT("LZCOUNT"),
CPUI_MAX;
CPUI_MAX(null);
private OpCode() {
private final String name;
private OpCode(String name) {
this.name = name;
}
public String getName() {
return get_opname(this);
return name;
}
public OpCode getOpCodeFlip()
{ // Return the complimentary opcode for boolean operations
// (or CPUI_MAX if not boolean) Set reorder to true if
// the complimentary operation would involve reordering
// the input parameters
switch (this) {
case CPUI_INT_EQUAL:
return CPUI_INT_NOTEQUAL;
case CPUI_INT_NOTEQUAL:
return CPUI_INT_EQUAL;
case CPUI_INT_SLESS:
return CPUI_INT_SLESSEQUAL;
case CPUI_INT_SLESSEQUAL:
return CPUI_INT_SLESS;
case CPUI_INT_LESS:
return CPUI_INT_LESSEQUAL;
case CPUI_INT_LESSEQUAL:
return CPUI_INT_LESS;
case CPUI_BOOL_NEGATE:
return CPUI_COPY;
case CPUI_FLOAT_EQUAL:
return CPUI_FLOAT_NOTEQUAL;
case CPUI_FLOAT_NOTEQUAL:
return CPUI_FLOAT_EQUAL;
case CPUI_FLOAT_LESS:
return CPUI_FLOAT_LESSEQUAL;
case CPUI_FLOAT_LESSEQUAL:
return CPUI_FLOAT_LESS;
default:
break;
}
return CPUI_MAX;
/**
* {@return the complimentary opcode for boolean operations}
*
* (or {@link #CPUI_MAX} if not boolean.) Set reorder to true if the complimentary operation
* would involve reordering the input parameters
*/
public OpCode getOpCodeFlip() {
return switch (this) {
case CPUI_INT_EQUAL -> CPUI_INT_NOTEQUAL;
case CPUI_INT_NOTEQUAL -> CPUI_INT_EQUAL;
case CPUI_INT_SLESS -> CPUI_INT_SLESSEQUAL;
case CPUI_INT_SLESSEQUAL -> CPUI_INT_SLESS;
case CPUI_INT_LESS -> CPUI_INT_LESSEQUAL;
case CPUI_INT_LESSEQUAL -> CPUI_INT_LESS;
case CPUI_BOOL_NEGATE -> CPUI_COPY;
case CPUI_FLOAT_EQUAL -> CPUI_FLOAT_NOTEQUAL;
case CPUI_FLOAT_NOTEQUAL -> CPUI_FLOAT_EQUAL;
case CPUI_FLOAT_LESS -> CPUI_FLOAT_LESSEQUAL;
case CPUI_FLOAT_LESSEQUAL -> CPUI_FLOAT_LESS;
default -> CPUI_MAX;
};
}
public boolean getBooleanFlip()
{ // Return the complimentary opcode for boolean operations
// (or CPUI_MAX if not boolean) Set reorder to true if
// the complimentary operation would involve reordering
// the input parameters
switch (this) {
case CPUI_INT_EQUAL:
return false;
case CPUI_INT_NOTEQUAL:
return false;
case CPUI_INT_SLESS:
return true;
case CPUI_INT_SLESSEQUAL:
return true;
case CPUI_INT_LESS:
return true;
case CPUI_INT_LESSEQUAL:
return true;
case CPUI_BOOL_NEGATE:
return false;
case CPUI_FLOAT_EQUAL:
return false;
case CPUI_FLOAT_NOTEQUAL:
return false;
case CPUI_FLOAT_LESS:
return true;
case CPUI_FLOAT_LESSEQUAL:
return true;
default:
break;
}
return false;
/**
* {@return the complimentary opcode for boolean operations}
*
* (or {@link #CPUI_MAX} if not boolean.) Set reorder to true if the complimentary operation
* would involve reordering the input parameters
*/
public boolean getBooleanFlip() {
return switch (this) {
case CPUI_INT_EQUAL -> false;
case CPUI_INT_NOTEQUAL -> false;
case CPUI_INT_SLESS -> true;
case CPUI_INT_SLESSEQUAL -> true;
case CPUI_INT_LESS -> true;
case CPUI_INT_LESSEQUAL -> true;
case CPUI_BOOL_NEGATE -> false;
case CPUI_FLOAT_EQUAL -> false;
case CPUI_FLOAT_NOTEQUAL -> false;
case CPUI_FLOAT_LESS -> true;
case CPUI_FLOAT_LESSEQUAL -> true;
default -> false;
};
}
static final String opcode_name[] = { "BLANK", "COPY", "LOAD", "STORE", "BRANCH", "CBRANCH",
"BRANCHIND", "CALL", "CALLIND", "CALLOTHER", "RETURN", "INT_EQUAL", "INT_NOTEQUAL",
"INT_SLESS", "INT_SLESSEQUAL", "INT_LESS", "INT_LESSEQUAL", "INT_ZEXT", "INT_SEXT",
"INT_ADD", "INT_SUB", "INT_CARRY", "INT_SCARRY", "INT_SBORROW", "INT_2COMP", "INT_NEGATE",
"INT_XOR", "INT_AND", "INT_OR", "INT_LEFT", "INT_RIGHT", "INT_SRIGHT", "INT_MULT",
"INT_DIV", "INT_SDIV", "INT_REM", "INT_SREM", "BOOL_NEGATE", "BOOL_XOR", "BOOL_AND",
"BOOL_OR", "FLOAT_EQUAL", "FLOAT_NOTEQUAL", "FLOAT_LESS", "FLOAT_LESSEQUAL", "UNUSED1",
"FLOAT_NAN", "FLOAT_ADD", "FLOAT_DIV", "FLOAT_MULT", "FLOAT_SUB", "FLOAT_NEG", "FLOAT_ABS",
"FLOAT_SQRT", "INT2FLOAT", "FLOAT2FLOAT", "TRUNC", "CEIL", "FLOOR", "ROUND", "BUILD",
"DELAY_SLOT", "PIECE", "SUBPIECE", "CAST", "LABEL", "CROSSBUILD", "SEGMENTOP", "CPOOLREF",
"NEW", "INSERT", "EXTRACT", "POPCOUNT", "LZCOUNT" };
static final List<OpCode> opsByOrdinal = List.of(OpCode.values());
static final Map<String, OpCode> opsByName =
Stream.of(OpCode.values())
.filter(op -> op != DO_NOT_USE_ME_I_AM_ENUM_ELEMENT_ZERO && op != CPUI_MAX)
.collect(Collectors.toUnmodifiableMap(OpCode::getName, op -> op));
public static String get_opname(OpCode op) {
return opcode_name[op.ordinal()];
public static OpCode getOpcode(int ordinal) {
return opsByOrdinal.get(ordinal);
}
static final int opcode_indices[] = { 0, 39, 37, 40, 38, 4, 6, 60, 7, 8, 9, 64, 5, 57, 1, 68,
66, 61, 71, 55, 52, 47, 48, 41, 43, 44, 49, 46, 51, 42, 53, 50, 58, 70, 54, 24, 19, 27, 21,
33, 11, 29, 15, 16, 32, 25, 12, 28, 35, 30, 23, 22, 34, 18, 13, 14, 36, 31, 20, 26, 17, 65,
2, 73, 69, 62, 72, 10, 59, 67, 3, 63, 56, 45 };
public static OpCode get_opcode(String nm) { // Use binary search to find name
int min = 1; // Don't include BLANK
int max = OpCode.CPUI_MAX.ordinal() - 1;
int cur, ind;
while (min <= max) { // Binary search
cur = (min + max) / 2;
ind = opcode_indices[cur]; // Get opcode in cur's sort slot
int result = opcode_name[ind].compareTo(nm);
if (result < 0) {
min = cur + 1; // Everything equal or below cur is less
}
else if (result > 0) {
max = cur - 1; // Everything equal or above cur is greater
}
else {
return OpCode.values()[ind]; // Found the match
public static OpCode getOpcode(String nm) {
return opsByName.get(nm);
}
}
return null; // Name isn't an op
}
}

View file

@ -28,8 +28,13 @@ import ghidra.program.model.pcode.Encoder;
public class ConstTpl {
@Override
public String toString() {
return "{type=" + type + " value_real=" + String.format("0x%x", value_real) + " spaceid=" +
spaceid + "}";
return switch (type) {
case real -> "ConstTpl[real=0x%x]".formatted(value_real);
case handle -> "ConstTpl[handle=%d,sel=%s]".formatted(handle_index, select);
case spaceid -> "ConstTpl[space=%s]".formatted(spaceid);
default -> "ConstTpl[type=%s,real=0x%x,space=%s,handle=%d,sel=%s]".formatted(type,
value_real, spaceid, handle_index, select);
};
}
public enum const_type {

View file

@ -44,7 +44,7 @@ public class OpTpl {
@Override
public String toString() {
return "OpTpl[opcode=" + opc + "]";
return "OpTpl[%s = %s %s]".formatted(output, opc, input);
}
public VarnodeTpl getOut() {
@ -129,7 +129,7 @@ public class OpTpl {
public void encode(Encoder encoder) throws IOException {
encoder.openElement(ELEM_OP_TPL);
encoder.writeOpcode(ATTRIB_CODE, opc.ordinal());
encoder.writeOpcode(ATTRIB_CODE, opc);
if (output == null) {
encoder.openElement(ELEM_NULL);
encoder.closeElement(ELEM_NULL);

View file

@ -15,7 +15,7 @@
*/
package ghidra.pcodeCPort.semantics;
import static ghidra.pcode.utils.SlaFormat.*;
import static ghidra.pcode.utils.SlaFormat.ELEM_VARNODE_TPL;
import java.io.IOException;
@ -210,13 +210,14 @@ public class VarnodeTpl {
if (obj == this) {
return true;
}
if (obj == null) {
if (!(obj instanceof VarnodeTpl op2)) {
return false;
}
if (!(obj instanceof VarnodeTpl)) {
return false;
return space.equals(op2.space) && offset.equals(op2.offset) && size.equals(op2.size);
}
VarnodeTpl o2 = (VarnodeTpl) obj;
return space.equals(o2.space) && offset.equals(o2.offset) && size.equals(o2.size);
@Override
public String toString() {
return "[space=%s:offset=%s:size=%s]".formatted(space, offset, size);
}
}

View file

@ -15,9 +15,11 @@
*/
package ghidra.pcodeCPort.slgh_compile;
import java.util.Iterator;
import java.util.*;
import java.util.Map.Entry;
import generic.stl.*;
import generic.stl.IteratorSTL;
import generic.stl.VectorSTL;
import ghidra.pcodeCPort.context.SleighError;
import ghidra.pcodeCPort.opcodes.OpCode;
import ghidra.pcodeCPort.semantics.*;
@ -39,10 +41,10 @@ class ConsistencyChecker {
private boolean printlargetempwarning; // if true, warning about temporary varnodes larger than SleighBase.MAX_UNIQUE_SIZE
private SleighCompile compiler;
private SubtableSymbol root_symbol;
private VectorSTL<SubtableSymbol> postorder = new VectorSTL<>();
private List<SubtableSymbol> postorder = new ArrayList<>();
// Sizes associated with tables
private MapSTL<SubtableSymbol, Integer> sizemap = new MapSTL<>((s1, s2) -> s1.compareTo(s2));
private Map<SubtableSymbol, Integer> sizemap = new HashMap<>();
private OperandSymbol getOperandSymbol(int slot, OpTpl op, Constructor ct) {
VarnodeTpl vn;
@ -519,37 +521,30 @@ class ConsistencyChecker {
}
private int recoverSize(ConstTpl sizeconst, Constructor ct) {
int size = 0, handindex;
OperandSymbol opsym;
SubtableSymbol tabsym;
IteratorSTL<Pair<SubtableSymbol, Integer>> iter;
return switch (sizeconst.getType()) {
case real -> (int) sizeconst.getReal();
case handle -> {
int handindex = sizeconst.getHandleIndex();
OperandSymbol opsym = ct.getOperand(handindex);
int size = opsym.getSize();
if (size != -1) {
yield size;
}
switch (sizeconst.getType()) {
case real:
size = (int) sizeconst.getReal();
break;
case handle:
handindex = sizeconst.getHandleIndex();
opsym = ct.getOperand(handindex);
size = opsym.getSize();
if (size == -1) {
TripleSymbol definingSymbol = opsym.getDefiningSymbol();
if (!(definingSymbol instanceof SubtableSymbol)) {
if (!(definingSymbol instanceof SubtableSymbol tabsym)) {
throw new SleighError("Could not recover varnode template size",
ct.location);
}
tabsym = (SubtableSymbol) definingSymbol;
iter = sizemap.find(tabsym);
if (iter.isEnd()) {
Integer symsize = sizemap.get(tabsym);
if (symsize == null) {
throw new SleighError("Subtable out of order", ct.location);
}
size = iter.get().second;
yield symsize;
}
break;
default:
throw new SleighError("Bad constant type as varnode template size", ct.location);
}
return size;
default -> throw new SleighError("Bad constant type as varnode template size",
ct.location);
};
}
private void handle(String msg, Constructor ct) {
@ -571,7 +566,7 @@ class ConsistencyChecker {
private boolean checkOpMisuse(OpTpl op, Constructor ct) {
switch (op.getOpcode()) {
case CPUI_INT_LESS: {
case CPUI_INT_LESS -> {
VarnodeTpl vn0 = op.getIn(0);
VarnodeTpl vn1 = op.getIn(1);
if (vn1.getSpace().isConstSpace()) {
@ -591,8 +586,7 @@ class ConsistencyChecker {
handleBetter("!= 0", ct);
}
}
break;
case CPUI_INT_LESSEQUAL: {
case CPUI_INT_LESSEQUAL -> {
VarnodeTpl vn0 = op.getIn(0);
VarnodeTpl vn1 = op.getIn(1);
if (vn0.getSpace().isConstSpace()) {
@ -612,9 +606,8 @@ class ConsistencyChecker {
handleBetter("== 0", ct);
}
}
break;
default:
break;
default -> {
}
}
return true;
}
@ -640,9 +633,10 @@ class ConsistencyChecker {
}
/**
* Returns true precisely when {@code opTpl} uses a {@link VarnodeTpl} in
* the unique space whose size is larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
* Note that this method returns as soon as one large {@link VarnodeTpl} is found.
* Returns true precisely when {@code opTpl} uses a {@link VarnodeTpl} in the unique space whose
* size is larger than {@link SleighBase#MAX_UNIQUE_SIZE}. Note that this method returns as soon
* as one large {@link VarnodeTpl} is found.
*
* @param opTpl the op to check
* @return true if {@code opTpl} uses a large temporary varnode
*/
@ -661,8 +655,9 @@ class ConsistencyChecker {
}
/**
* Returns true precisely when {@code vn} is in the unique space
* and has a size larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
* Returns true precisely when {@code vn} is in the unique space and has a size larger than
* {@link SleighBase#MAX_UNIQUE_SIZE}.
*
* @param vn varnode template to check
* @return true if it uses a large temporary
*/
@ -836,7 +831,7 @@ class ConsistencyChecker {
path.pop_back(); // Table is fully traversed
state.pop_back();
ctstate.pop_back();
postorder.push_back(cur); // Post the traversed table
postorder.add(cur); // Post the traversed table
}
else {
Constructor ct = cur.getConstructor(ctind);
@ -849,11 +844,9 @@ class ConsistencyChecker {
ctstate.setBack(oper + 1);
OperandSymbol opsym = ct.getOperand(oper);
TripleSymbol definingSymbol = opsym.getDefiningSymbol();
if (definingSymbol instanceof SubtableSymbol) {
SubtableSymbol subsym = (SubtableSymbol) definingSymbol;
IteratorSTL<Pair<SubtableSymbol, Integer>> iter;
iter = sizemap.find(subsym);
if (iter.isEnd()) { // Not traversed yet
if (definingSymbol instanceof SubtableSymbol subsym) {
Integer symsize = sizemap.get(subsym);
if (symsize == null) { // Not traversed yet
sizemap.put(subsym, -1); // Mark table as
// traversed
path.push_back(subsym); // Recurse
@ -866,8 +859,97 @@ class ConsistencyChecker {
}
}
static class UniqueState {
NavigableMap<Long, OptimizeRecord> recs = new TreeMap<>();
private static long endOf(Entry<Long, OptimizeRecord> entry) {
return entry.getKey() + entry.getValue().size;
}
public void clear() {
recs.clear();
}
/**
* Combine all the given entries into one, where the varnode is the union of all the given
* varnodes.
*
* <p>
* NOTE: There may be a weird case where two neighboring varnodes are read, then later they
* get coalesced in a write. This would indicate the combined varnode is read twice, which
* is not exactly correct. However, the optimizer would exclude that case, anyway.
*
* @param records the entries
* @return the coalesced entry
*/
private OptimizeRecord coalesce(List<OptimizeRecord> records) {
long minOff = -1;
long maxOff = -1;
for (OptimizeRecord rec : records) {
if (minOff == -1 || rec.offset < minOff) {
minOff = rec.offset;
}
if (maxOff == -1 || rec.offset + rec.size > maxOff) {
maxOff = rec.offset + rec.size;
}
}
OptimizeRecord result = new OptimizeRecord(minOff, (int) (maxOff - minOff));
for (OptimizeRecord rec : records) {
result.updateCombine(rec);
}
return result;
}
private void set(long offset, int size, OptimizeRecord rec) {
List<OptimizeRecord> records = getDefinitions(offset, size);
records.add(rec);
OptimizeRecord coalesced = coalesce(records);
recs.subMap(coalesced.offset, coalesced.offset + coalesced.size).clear();
recs.put(coalesced.offset, coalesced);
}
private List<OptimizeRecord> getDefinitions(long offset, int size) {
if (size == 0) {
size = 1;
}
List<OptimizeRecord> result = new ArrayList<>();
Entry<Long, OptimizeRecord> preEntry = recs.lowerEntry(offset);
long cursor = offset;
if (preEntry != null && endOf(preEntry) > offset) {
OptimizeRecord preRec = preEntry.getValue();
// No need to truncate, as we're just counting a read
// Do not overwrite in map for reads
cursor = endOf(preEntry);
// Make an immutable copy of the entry.
result.add(preRec);
}
long end = offset + size;
Map<Long, OptimizeRecord> toPut = new HashMap<>();
for (Entry<Long, OptimizeRecord> entry : recs.subMap(offset, end).entrySet()) {
if (entry.getKey() > cursor) {
// This will certainly cause an error report. Good.
OptimizeRecord missing =
new OptimizeRecord(cursor, (int) (entry.getKey() - cursor));
toPut.put(cursor, missing);
result.add(missing);
}
// No need to truncate, as we're just counting a read
result.add(entry.getValue());
cursor = endOf(entry);
}
if (end > cursor) {
OptimizeRecord missing = new OptimizeRecord(cursor, (int) (end - cursor));
toPut.put(cursor, missing);
result.add(missing);
}
recs.putAll(toPut);
assert !result.isEmpty();
return result;
}
}
// Optimization routines
private static void examineVn(MapSTL<Long, OptimizeRecord> recs, VarnodeTpl vn, int i,
private static void examineVn(UniqueState state, VarnodeTpl vn, int i,
int inslot, int secnum) {
if (vn == null) {
return;
@ -879,22 +961,17 @@ class ConsistencyChecker {
return;
}
IteratorSTL<Pair<Long, OptimizeRecord>> iter;
iter = recs.find(vn.getOffset().getReal());
if (iter.isEnd()) {
recs.put(vn.getOffset().getReal(), new OptimizeRecord());
iter = recs.find(vn.getOffset().getReal());
}
long offset = vn.getOffset().getReal();
int size = (int) vn.getSize().getReal();
if (inslot >= 0) {
iter.get().second.readop = i;
iter.get().second.readcount += 1;
iter.get().second.inslot = inslot;
iter.get().second.readsection = secnum;
for (OptimizeRecord rec : state.getDefinitions(offset, size)) {
rec.updateRead(i, inslot, secnum);
}
}
else {
iter.get().second.writeop = i;
iter.get().second.writecount += 1;
iter.get().second.writesection = secnum;
OptimizeRecord rec = new OptimizeRecord(offset, size);
rec.updateWrite(i, secnum);
state.set(offset, size, rec);
}
}
@ -997,7 +1074,7 @@ class ConsistencyChecker {
}
// Look for reads and writes to temporaries
private void optimizeGather1(Constructor ct, MapSTL<Long, OptimizeRecord> recs, int secnum) {
private void optimizeGather1(Constructor ct, UniqueState state, int secnum) {
ConstructTpl tpl;
if (secnum < 0) {
tpl = ct.getTempl();
@ -1013,15 +1090,15 @@ class ConsistencyChecker {
OpTpl op = ops.get(i);
for (int j = 0; j < op.numInput(); ++j) {
VarnodeTpl vnin = op.getIn(j);
examineVn(recs, vnin, i, j, secnum);
examineVn(state, vnin, i, j, secnum);
}
VarnodeTpl vn = op.getOut();
examineVn(recs, vn, i, -1, secnum);
examineVn(state, vn, i, -1, secnum);
}
}
// Make sure any temp used by the export is not optimized away
private void optimizeGather2(Constructor ct, MapSTL<Long, OptimizeRecord> recs, int secnum) {
private void optimizeGather2(Constructor ct, UniqueState state, int secnum) {
ConstructTpl tpl;
if (secnum < 0) {
tpl = ct.getTempl();
@ -1039,39 +1116,31 @@ class ConsistencyChecker {
if (hand.getPtrSpace().isUniqueSpace()) {
if (hand.getPtrOffset().getType() == ConstTpl.const_type.real) {
long offset = hand.getPtrOffset().getReal();
recs.put(offset, new OptimizeRecord());
IteratorSTL<Pair<Long, OptimizeRecord>> res = recs.find(offset);
res.get().second.writeop = 0;
res.get().second.readop = 0;
res.get().second.writecount = 2;
res.get().second.readcount = 2;
res.get().second.readsection = -2;
res.get().second.writesection = -2;
int size = (int) hand.getPtrSize().getReal();
for (OptimizeRecord rec : state.getDefinitions(offset, size)) {
rec.updateExport();
// NOTE: Could this just be updateRead?
// Technically, an exported handle could be written by the parent....
}
}
}
if (hand.getSpace().isUniqueSpace()) {
if ((hand.getPtrSpace().getType() == ConstTpl.const_type.real) &&
(hand.getPtrOffset().getType() == ConstTpl.const_type.real)) {
long offset = hand.getPtrOffset().getReal();
recs.put(offset, new OptimizeRecord());
IteratorSTL<Pair<Long, OptimizeRecord>> res = recs.find(offset);
res.get().second.writeop = 0;
res.get().second.readop = 0;
res.get().second.writecount = 2;
res.get().second.readcount = 2;
res.get().second.readsection = -2;
res.get().second.writesection = -2;
int size = (int) hand.getPtrSize().getReal();
for (OptimizeRecord rec : state.getDefinitions(offset, size)) {
rec.updateExport();
// NOTE: Could this just be updateRead?
// Technically, an exported handle could be written by the parent....
}
}
}
}
private OptimizeRecord findValidRule(Constructor ct, MapSTL<Long, OptimizeRecord> recs) {
IteratorSTL<Pair<Long, OptimizeRecord>> iter;
iter = recs.begin();
while (!iter.isEnd()) {
OptimizeRecord currec = iter.get().second;
iter.increment();
private OptimizeRecord findValidRule(Constructor ct, UniqueState state) {
for (Entry<Long, OptimizeRecord> ent : state.recs.entrySet()) {
OptimizeRecord currec = ent.getValue();
if ((currec.writecount == 1) && (currec.readcount == 1) &&
(currec.readsection == currec.writesection)) {
// Temporary must be read and written exactly once
@ -1083,14 +1152,33 @@ class ConsistencyChecker {
tpl = ct.getNamedTempl(currec.readsection);
}
VectorSTL<OpTpl> ops = tpl.getOpvec();
OpTpl op = ops.get(currec.readop);
OpTpl writeop = ops.get(currec.writeop);
OpTpl readop = ops.get(currec.readop);
if (currec.writeop >= currec.readop) {
throw new SleighError("Read of temporary before write", ct.location);
}
if (op.getOpcode() == OpCode.CPUI_COPY) {
VarnodeTpl writevn = writeop.getOut();
VarnodeTpl readvn = readop.getIn(currec.inslot);
/**
* Because the record can change size and position, we have to check if the varnode
* "connecting" the write and read ops is actually the same varnode. If not, then we
* can't optimize it out.
*
* There may be an opportunity here to re-write the size/offset when either the
* write or read op is a COPY, but I'll leave that for later discussion.
*
* Actually, maybe not. If the truncation would be of a handle, we can't.
*/
if (!Objects.equals(writevn, readvn)) {
continue;
}
if (readop.getOpcode() == OpCode.CPUI_COPY) {
boolean saverecord = true;
currec.opttype = 0;
VarnodeTpl vn = op.getOut();
VarnodeTpl vn = readop.getOut();
for (int i = currec.writeop + 1; i < currec.readop; ++i) {
if (readWriteInterference(vn, ops.get(i), true)) {
saverecord = false;
@ -1101,11 +1189,10 @@ class ConsistencyChecker {
return currec;
}
}
op = ops.get(currec.writeop);
if (op.getOpcode() == OpCode.CPUI_COPY) {
if (writeop.getOpcode() == OpCode.CPUI_COPY) {
boolean saverecord = true;
currec.opttype = 1;
VarnodeTpl vn = op.getIn(0);
VarnodeTpl vn = writeop.getIn(0);
for (int i = currec.writeop + 1; i < currec.readop; ++i) {
if (readWriteInterference(vn, ops.get(i), false)) {
saverecord = false;
@ -1148,11 +1235,8 @@ class ConsistencyChecker {
ctempl.deleteOps(deleteops);
}
private void checkUnusedTemps(Constructor ct, MapSTL<Long, OptimizeRecord> recs) {
IteratorSTL<Pair<Long, OptimizeRecord>> iter = recs.begin();
while (!iter.isEnd()) {
Pair<Long, OptimizeRecord> pair = iter.get();
OptimizeRecord currec = pair.second;
private void checkUnusedTemps(Constructor ct, UniqueState state) {
for (OptimizeRecord currec : state.recs.values()) {
if (currec.readcount == 0) {
if (printdeadwarning) {
compiler.reportWarning(ct.location, "Temporary is written but not read");
@ -1163,13 +1247,13 @@ class ConsistencyChecker {
compiler.reportError(ct.location, "Temporary is read but not written");
readnowrite += 1;
}
iter.increment();
}
}
/**
* Checks {@code ct} to see whether p-code section contains an {@link OpTpl} which
* uses a varnode in the unique space which is larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
* Checks {@code ct} to see whether p-code section contains an {@link OpTpl} which uses a
* varnode in the unique space which is larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
*
* @param ct constructor to check
* @param ctpl is the specific p-code section
*/
@ -1190,21 +1274,21 @@ class ConsistencyChecker {
private void optimize(Constructor ct) {
OptimizeRecord currec;
MapSTL<Long, OptimizeRecord> recs = new ComparableMapSTL<>();
UniqueState state = new UniqueState();
int numsections = ct.getNumSections();
do {
recs.clear();
state.clear();
for (int i = -1; i < numsections; ++i) {
optimizeGather1(ct, recs, i);
optimizeGather2(ct, recs, i);
optimizeGather1(ct, state, i);
optimizeGather2(ct, state, i);
}
currec = findValidRule(ct, recs);
currec = findValidRule(ct, state);
if (currec != null) {
applyOptimization(ct, currec);
}
}
while (currec != null);
checkUnusedTemps(ct, recs);
checkUnusedTemps(ct, state);
}
public ConsistencyChecker(SleighCompile cp, SubtableSymbol rt, boolean unnecessary,
@ -1319,8 +1403,9 @@ class ConsistencyChecker {
}
/**
* Returns the number of constructors which reference a varnode in the
* unique space with size larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
* Returns the number of constructors which reference a varnode in the unique space with size
* larger than {@link SleighBase#MAX_UNIQUE_SIZE}.
*
* @return num constructors with large temp varnodes
*/
public int getNumLargeTemporaries() {

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +16,8 @@
package ghidra.pcodeCPort.slgh_compile;
class OptimizeRecord {
final long offset;
final int size;
int writeop;
int readop;
int inslot;
@ -26,7 +27,10 @@ class OptimizeRecord {
int readsection;
int opttype;
OptimizeRecord() {
OptimizeRecord(long offset, int size) {
this.offset = offset;
this.size = size;
writeop = -1;
readop = -1;
inslot = -1;
@ -37,6 +41,55 @@ class OptimizeRecord {
opttype = -1;
}
public void copyFromExcludingSize(OptimizeRecord that) {
this.writeop = that.writeop;
this.readop = that.readop;
this.inslot = that.inslot;
this.writecount = that.writecount;
this.readcount = that.readcount;
this.writesection = that.writesection;
this.readsection = that.readsection;
this.opttype = that.opttype;
}
public void updateRead(int i, int inslot, int secNum) {
assert inslot >= 0;
this.readop = i;
this.readcount += 1;
this.inslot = inslot;
this.readsection = secNum;
}
public void updateWrite(int i, int secNum) {
this.writeop = i;
this.writecount += 1;
this.writesection = secNum;
}
public void updateExport() {
this.writeop = 0;
this.readop = 0;
this.writecount = 2;
this.readcount = 2;
this.readsection = -2;
this.writesection = -2;
}
public void updateCombine(OptimizeRecord that) {
if (that.writecount != 0) {
this.writeop = that.writeop;
this.writesection = that.writesection;
}
if (that.readcount != 0) {
this.readop = that.readop;
this.inslot = that.inslot;
this.readsection = that.readsection;
}
this.writecount += that.writecount;
this.readcount += that.readcount;
// opttype is not relevant here
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();

View file

@ -470,10 +470,6 @@ public abstract class PcodeCompile {
return null;
}
if (basevn.getSpace().isUniqueSpace()) {
return null;
}
const_type offset_type = basevn.getOffset().getType();
if ((offset_type != const_type.real) && (offset_type != const_type.handle)) {
return null;

View file

@ -22,7 +22,7 @@ import java.io.IOException;
import ghidra.program.model.pcode.Encoder;
import ghidra.sleigh.grammar.Location;
public class SleighSymbol implements Comparable<SleighSymbol> {
public class SleighSymbol {
@Override
public String toString() {
return name;
@ -85,11 +85,6 @@ public class SleighSymbol implements Comparable<SleighSymbol> {
encodeSleighSymbolHeader(encoder);
}
@Override
public int compareTo(SleighSymbol o) {
return id - o.id;
}
public final Location location;
public Location getLocation() {

View file

@ -17,42 +17,47 @@ package ghidra.program.model.pcode;
import java.io.IOException;
import ghidra.pcodeCPort.opcodes.OpCode;
import ghidra.program.model.address.AddressSpace;
/**
* An interface for writing structured data to a stream
*
* The resulting encoded data is structured similarly to an XML document. The document contains a nested set
* of \elements, with labels corresponding to the ElementId class. A single element can hold
* zero or more attributes and zero or more child elements. An attribute holds a primitive
* data element (boolean, long, String) and is labeled by an AttributeId. The document is written
* using a sequence of openElement() and closeElement() calls, intermixed with write*() calls to encode
* the data primitives. All primitives written using a write*() call are associated with current open element,
* and all write*() calls for one element must come before opening any child element.
* The traditional XML element text content can be written using the special ATTRIB_CONTENT AttributeId, which
* must be the last write*() call associated with the specific element.
* <p>
* The resulting encoded data is structured similarly to an XML document. The document contains a
* nested set of <em>elements</em>, with labels corresponding to the ElementId class. A single
* element can hold zero or more attributes and zero or more child elements. An attribute holds a
* primitive data element (boolean, long, String) and is labeled by an AttributeId. The document is
* written using a sequence of openElement() and closeElement() calls, intermixed with write*()
* calls to encode the data primitives. All primitives written using a write*() call are associated
* with current open element, and all write*() calls for one element must come before opening any
* child element. The traditional XML element text content can be written using the special
* ATTRIB_CONTENT AttributeId, which must be the last write*() call associated with the specific
* element.
*/
public interface Encoder {
/**
* Begin a new element in the encoding
* The element will have the given ElementId annotation and becomes the \e current element.
* Begin a new element in the encoding The element will have the given ElementId annotation and
* becomes the \e current element.
*
* @param elemId is the given ElementId annotation
* @throws IOException for errors in the underlying stream
*/
void openElement(ElementId elemId) throws IOException;
/**
* End the current element in the encoding
* The current element must match the given annotation or an exception is thrown.
* End the current element in the encoding The current element must match the given annotation
* or an exception is thrown.
*
* @param elemId is the given (expected) annotation for the current element
* @throws IOException for errors in the underlying stream
*/
void closeElement(ElementId elemId) throws IOException;
/**
* Write an annotated boolean value into the encoding
* The boolean data is associated with the given AttributeId annotation and the current open element.
* Write an annotated boolean value into the encoding The boolean data is associated with the
* given AttributeId annotation and the current open element.
*
* @param attribId is the given AttributeId annotation
* @param val is boolean value to encode
* @throws IOException for errors in the underlying stream
@ -60,8 +65,9 @@ public interface Encoder {
void writeBool(AttributeId attribId, boolean val) throws IOException;
/**
* Write an annotated signed integer value into the encoding
* The integer is associated with the given AttributeId annotation and the current open element.
* Write an annotated signed integer value into the encoding The integer is associated with the
* given AttributeId annotation and the current open element.
*
* @param attribId is the given AttributeId annotation
* @param val is the signed integer value to encode
* @throws IOException for errors in the underlying stream
@ -69,8 +75,9 @@ public interface Encoder {
void writeSignedInteger(AttributeId attribId, long val) throws IOException;
/**
* Write an annotated unsigned integer value into the encoding
* The integer is associated with the given AttributeId annotation and the current open element.
* Write an annotated unsigned integer value into the encoding The integer is associated with
* the given AttributeId annotation and the current open element.
*
* @param attribId is the given AttributeId annotation
* @param val is the unsigned integer value to encode
* @throws IOException for errors in the underlying stream
@ -78,8 +85,9 @@ public interface Encoder {
void writeUnsignedInteger(AttributeId attribId, long val) throws IOException;
/**
* Write an annotated string into the encoding
* The string is associated with the given AttributeId annotation and the current open element.
* Write an annotated string into the encoding The string is associated with the given
* AttributeId annotation and the current open element.
*
* @param attribId is the given AttributeId annotation
* @param val is the string to encode
* @throws IOException for errors in the underlying stream
@ -87,11 +95,12 @@ public interface Encoder {
void writeString(AttributeId attribId, String val) throws IOException;
/**
* Write an annotated string, using an indexed attribute, into the encoding.
* Multiple attributes with a shared name can be written to the same element by calling this
* method multiple times with a different index value. The encoding will use attribute ids up
* to the base id plus the maximum index passed in. Implementors must be careful to not use
* other attributes with ids bigger than the base id within the element taking the indexed attribute.
* Write an annotated string, using an indexed attribute, into the encoding. Multiple attributes
* with a shared name can be written to the same element by calling this method multiple times
* with a different index value. The encoding will use attribute ids up to the base id plus the
* maximum index passed in. Implementors must be careful to not use other attributes with ids
* bigger than the base id within the element taking the indexed attribute.
*
* @param attribId is the shared AttributeId
* @param index is the unique index to associated with the string
* @param val is the string to encode
@ -100,8 +109,9 @@ public interface Encoder {
void writeStringIndexed(AttributeId attribId, int index, String val) throws IOException;
/**
* Write an address space reference into the encoding
* The address space is associated with the given AttributeId annotation and the current open element.
* Write an address space reference into the encoding The address space is associated with the
* given AttributeId annotation and the current open element.
*
* @param attribId is the given AttributeId annotation
* @param spc is the address space to encode
* @throws IOException for errors in the underlying stream
@ -109,9 +119,9 @@ public interface Encoder {
void writeSpace(AttributeId attribId, AddressSpace spc) throws IOException;
/**
* Write an address space reference into the encoding.
* An address space identified by its name and unique index is associated with the given
* annotation and the current open element.
* Write an address space reference into the encoding. An address space identified by its name
* and unique index is associated with the given annotation and the current open element.
*
* @param attribId is the given annotation
* @param index is the unique index of the address space
* @param name is the name of the address space
@ -120,11 +130,12 @@ public interface Encoder {
void writeSpace(AttributeId attribId, int index, String name) throws IOException;
/**
* Write a p-code operation opcode into the encoding, associating it with the given
* annotation. The opcode is specified based on the constants defined in {@link PcodeOp}.
* Write a p-code operation opcode into the encoding, associating it with the given annotation.
* The opcode is specified based on the constants defined in {@link PcodeOp}.
*
* @param attribId is the given annotation
* @param opcode is the opcode constant
* @throws IOException for errors in the underlying stream
*/
void writeOpcode(AttributeId attribId, int opcode) throws IOException;
void writeOpcode(AttributeId attribId, OpCode opcode) throws IOException;
}

View file

@ -20,11 +20,12 @@ import static ghidra.program.model.pcode.PackedDecode.*;
import java.io.IOException;
import java.io.OutputStream;
import ghidra.pcodeCPort.opcodes.OpCode;
import ghidra.program.model.address.AddressSpace;
/**
* A byte-based encoder designed to marshal to the decompiler efficiently
* See {@code PackedDecode} for details of the encoding format
* A byte-based encoder designed to marshal to the decompiler efficiently See {@code PackedDecode}
* for details of the encoding format
*/
public class PackedEncode implements Encoder {
protected OutputStream outStream;
@ -201,9 +202,9 @@ public class PackedEncode implements Encoder {
}
@Override
public void writeOpcode(AttributeId attribId, int opcode) throws IOException {
public void writeOpcode(AttributeId attribId, OpCode opcode) throws IOException {
writeHeader(ATTRIBUTE, attribId.id());
writeInteger((TYPECODE_SIGNEDINT_POSITIVE << TYPECODE_SHIFT), opcode);
writeInteger((TYPECODE_SIGNEDINT_POSITIVE << TYPECODE_SHIFT), opcode.ordinal());
}
/**

View file

@ -15,19 +15,19 @@
*/
package ghidra.program.model.pcode;
import static ghidra.program.model.pcode.AttributeId.*;
import static ghidra.program.model.pcode.AttributeId.ATTRIB_CONTENT;
import java.io.IOException;
import java.io.OutputStream;
import ghidra.pcodeCPort.opcodes.OpCode;
import ghidra.program.model.address.AddressSpace;
import ghidra.util.xml.SpecXmlUtils;
/**
* An XML based encoder
* The underlying transfer encoding is an XML document.
* The encoder is initialized with a StringBuilder which will receive the XML document as calls
* are made on the encoder.
* An XML based encoder The underlying transfer encoding is an XML document. The encoder is
* initialized with a StringBuilder which will receive the XML document as calls are made on the
* encoder.
*/
public class XmlEncode implements CachedEncoder {
@ -233,8 +233,8 @@ public class XmlEncode implements CachedEncoder {
}
@Override
public void writeOpcode(AttributeId attribId, int opcode) throws IOException {
String name = PcodeOp.getMnemonic(opcode);
public void writeOpcode(AttributeId attribId, OpCode opcode) throws IOException {
String name = opcode.getName();
if (attribId == ATTRIB_CONTENT) {
if (tagStatus == TAG_START) {
buffer.append('>');

View file

@ -952,7 +952,7 @@ cc: "E" is cond=15 { tmp:1 = ($(Z) == 1); export tmp; }
:POPF is op8=0xf3 {
local result:2 = 0;
pop(result);
PSW = result:1;
PSW = zext(result:1);
local resultHi = result >> 8;
INT_MASK = resultHi:1;
}

View file

@ -3456,8 +3456,8 @@ define pcodeop contextswap;
local div:2 = sext(TOK_10_7_Wreg) s/ sext(TOK_3_0_Wreg);
local rem:2 = sext(TOK_10_7_Wreg) s% sext(TOK_3_0_Wreg);
W0 = div:1;
W1 = rem:1;
W0 = zext(div:1);
W1 = zext(rem:1);
testSRL_N ( W1 );
@ -3545,8 +3545,8 @@ define pcodeop isDivideOverflow;
local div:2 = zext(TOK_10_7_Wreg) / zext(TOK_3_0_Wreg);
local rem:2 = zext(TOK_10_7_Wreg) % zext(TOK_3_0_Wreg);
W0 = div:1;
W1 = rem:1;
W0 = zext(div:1);
W1 = zext(rem:1);
testSRL_N ( W1 );
@ -7204,13 +7204,13 @@ define pcodeop pwrsavOp;
:sac.d ACCA_t^r4_t,Wsnd_t is OP_23_16=0xDC & OP_14=0x0 & OP_7_4=0x0 & ACCA_t & r4_t & Wsnd_t {
local tmp:6 = ACCA s>> (16 + r4_t);
Wsnd_t = tmp:2;
Wsnd_t = tmp:4;
}
:sac.d ACCB_t^r4_t,Wsnd_t is OP_23_16=0xDC & OP_14=0x0 & OP_7_4=0x0 & ACCB_t & r4_t & Wsnd_t {
local tmp:6 = ACCB s>> (16 + r4_t);
Wsnd_t = tmp:2;
Wsnd_t = tmp:4;
}
@endif

View file

@ -2088,9 +2088,9 @@ define pcodeop VectorMultiplyWordHighSignedSaturateFractionalToAccumulator2;
# RT.h = temp.l;
lo:$(REGISTER_SIZE) = (( A & (0x0000000000000000) ) >> 32) * (( B & (0x0000000000000000) ) >> 32);
lo = lo:4;
lo = zext(lo:4);
hi:$(REGISTER_SIZE) = (( A & (0xFFFFFFFF00000000) ) >> 32) * (( B & (0xFFFFFFFF00000000) ) >> 32);
hi = hi:4;
hi = zext(hi:4);
D = (( zext(hi) << 32) | zext(lo) );
}
@ -2106,9 +2106,9 @@ define pcodeop VectorMultiplyWordHighSignedSaturateFractionalToAccumulator2;
# ACC = RT;
lo:$(REGISTER_SIZE) = (( A & (0x0000000000000000) ) >> 32) * (( B & (0x0000000000000000) ) >> 32);
lo = lo:4;
lo = zext(lo:4);
hi:$(REGISTER_SIZE) = (( A & (0xFFFFFFFF00000000) ) >> 32) * (( B & (0xFFFFFFFF00000000) ) >> 32);
hi = hi:4;
hi = zext(hi:4);
D = (( zext(hi) << 32) | zext(lo) );
ACC = D;
}

View file

@ -1764,7 +1764,8 @@ define pcodeop altv300_71;
{
local offs:2 = (12 - zext(A[0,4])) * 8;
local out:16 = (vrB >> offs) & 0xffffffff;
D = out:4;
# No need for zext, as mask is already applied
D = out:$(REGISTER_SIZE);
}
:vextuwrx D,A,vrB is OP=4 & D & A & vrB & XOP_0_10=1933 {