diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc index ca9d71ab99..a67a3de849 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/pcodecompile.cc @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc index cd9b9835b1..18e2ff8ba1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.cc @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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 { diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh index e0b069959d..b53b18797d 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/semantics.hh @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -91,6 +91,8 @@ public: bool isDynamic(const ParserWalker &walker) const; int4 transfer(const vector ¶ms); bool isZeroSize(void) const { return size.isZero(); } + bool operator==(const VarnodeTpl &op2) const; + bool operator!=(const VarnodeTpl &op2) const; bool operator<(const VarnodeTpl &op2) const; void setOffset(uintb constVal) { offset = ConstTpl(ConstTpl::real,constVal); } void setRelative(uintb constVal) { offset = ConstTpl(ConstTpl::j_relative,constVal); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc index 50d85e22ba..d8de0d5fed 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/slgh_compile.cc @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -180,6 +180,75 @@ SubtableSymbol *WithBlock::getCurrentSubtable(const list &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::iterator ConsistencyChecker::UniqueState::lesserIter(uintb offset) + +{ + if (recs.begin() == recs.end()) { + return recs.end(); + } + map::iterator iter; + iter = recs.lower_bound(offset); + if (iter == recs.begin()) { + return recs.end(); + } + return std::prev(iter); +} + +ConsistencyChecker::OptimizeRecord ConsistencyChecker::UniqueState::coalesce(vector &records) + +{ + uintb minOff = -1; + uintb maxOff = -1; + vector::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 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(coalesced.offset, coalesced)); +} + +void ConsistencyChecker::UniqueState::getDefinitions(vector &result, uintb offset, int4 size) + +{ + if (size == 0) { + size = 1; + } + map::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(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(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 &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::iterator iter; - iter = recs.insert( pair(vn->getOffset().getReal(),OptimizeRecord())).first; - if (inslot>=0) { - (*iter).second.readop = i; - (*iter).second.readcount += 1; - (*iter).second.inslot = inslot; - (*iter).second.readsection = secnum; + uintb offset = vn->getOffset().getReal(); + int4 size = vn->getSize().getReal(); + if (inslot >= 0) { + vector defs; + state.getDefinitions(defs,offset,size); + for (vector::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 &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 &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,mapnumInput();++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 &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,mapgetPtrSpace().isUniqueSpace()) { if (hand->getPtrOffset().getType() == ConstTpl::real) { - pair::iterator,bool> res; uintb offset = hand->getPtrOffset().getReal(); - res = recs.insert( pair(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 defs; + state.getDefinitions(defs,offset,size); + for (vector::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::iterator,bool> res; uintb offset = hand->getPtrOffset().getReal(); - res = recs.insert( pair(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 defs; + state.getDefinitions(defs,offset,size); + for (vector::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 &recs) const + const UniqueState &state) const { map::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 &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;igetOpcode() == 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 &recs) +/// \param state is the collection of records associated with each temporary Varnode +void ConsistencyChecker::checkUnusedTemps(Constructor *ct,const UniqueState &state) { map::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 recs; + UniqueState state; int4 numsections = ct->getNumSections(); do { - recs.clear(); + state.clear(); for(int4 i=-1;i #include +#include 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,26 +139,51 @@ 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 { - 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) - int4 writecount; ///< Number of times the register is written - int4 readcount; ///< Number of times the register is read - int4 writesection; ///< Section containing (last) p-code op writing to the register (or -2) - int4 readsection; ///< Section containing (last) p-code op reading the register (or -2) - mutable int4 opttype; ///< 0 = register read by a COPY, 1 = register written by a COPY (-1 otherwise) + 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) + int4 writecount; ///< Number of times the register is written + int4 readcount; ///< Number of times the register is read + int4 writesection; ///< Section containing (last) p-code op writing to the register (or -2) + int4 readsection; ///< Section containing (last) p-code op reading the register (or -2) + 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 recs; + static uintb endOf(map::iterator &iter) { return iter->first + iter->second.size; } + OptimizeRecord coalesce(vector &records); + map::iterator lesserIter(uintb offset); + public: + void clear(void) { recs.clear(); } + void set(uintb offset, int4 size, OptimizeRecord &rec); + void getDefinitions(vector &result, uintb offset, int4 size); + map::const_iterator begin(void) const { return recs.begin(); } + map::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 &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 &recs,int4 secnum) const; - void optimizeGather2(Constructor *ct,map &recs,int4 secnum) const; - const OptimizeRecord *findValidRule(Constructor *ct,const map &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 &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 diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/opcodes/OpCode.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/opcodes/OpCode.java index d69279a188..53ae1f5fbb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/opcodes/OpCode.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/opcodes/OpCode.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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 opsByOrdinal = List.of(OpCode.values()); + static final Map 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 - } - } - return null; // Name isn't an op + public static OpCode getOpcode(String nm) { + return opsByName.get(nm); } - } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/ConstTpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/ConstTpl.java index 4c84b4d94e..0c16eeb845 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/ConstTpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/ConstTpl.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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 { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/OpTpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/OpTpl.java index dcca4e5478..04e88c54e9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/OpTpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/OpTpl.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/VarnodeTpl.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/VarnodeTpl.java index 4e5b2d08fa..34c690d49e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/VarnodeTpl.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/semantics/VarnodeTpl.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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; - } - VarnodeTpl o2 = (VarnodeTpl) obj; - return space.equals(o2.space) && offset.equals(o2.offset) && size.equals(o2.size); + return space.equals(op2.space) && offset.equals(op2.offset) && size.equals(op2.size); + } + + @Override + public String toString() { + return "[space=%s:offset=%s:size=%s]".formatted(space, offset, size); } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/ConsistencyChecker.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/ConsistencyChecker.java index 0c28b1734d..dfad7abd0e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/ConsistencyChecker.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/ConsistencyChecker.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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 postorder = new VectorSTL<>(); + private List postorder = new ArrayList<>(); // Sizes associated with tables - private MapSTL sizemap = new MapSTL<>((s1, s2) -> s1.compareTo(s2)); + private Map 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> iter; - - 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)) { - throw new SleighError("Could not recover varnode template size", - ct.location); - } - tabsym = (SubtableSymbol) definingSymbol; - iter = sizemap.find(tabsym); - if (iter.isEnd()) { - throw new SleighError("Subtable out of order", ct.location); - } - size = iter.get().second; + 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; } - break; - default: - throw new SleighError("Bad constant type as varnode template size", ct.location); - } - return size; + + TripleSymbol definingSymbol = opsym.getDefiningSymbol(); + if (!(definingSymbol instanceof SubtableSymbol tabsym)) { + throw new SleighError("Could not recover varnode template size", + ct.location); + } + Integer symsize = sizemap.get(tabsym); + if (symsize == null) { + throw new SleighError("Subtable out of order", ct.location); + } + yield symsize; + } + 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> 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 recs = new TreeMap<>(); + + private static long endOf(Entry 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. + * + *

+ * 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 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 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 getDefinitions(long offset, int size) { + if (size == 0) { + size = 1; + } + List result = new ArrayList<>(); + Entry 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 toPut = new HashMap<>(); + for (Entry 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 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> 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 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 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> 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> 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 recs) { - IteratorSTL> iter; - iter = recs.begin(); - while (!iter.isEnd()) { - OptimizeRecord currec = iter.get().second; - iter.increment(); - + private OptimizeRecord findValidRule(Constructor ct, UniqueState state) { + for (Entry 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 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 recs) { - IteratorSTL> iter = recs.begin(); - while (!iter.isEnd()) { - Pair 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 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() { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/OptimizeRecord.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/OptimizeRecord.java index 88ac59b8ce..98de48f2f8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/OptimizeRecord.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/OptimizeRecord.java @@ -1,13 +1,12 @@ /* ### * 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. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/PcodeCompile.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/PcodeCompile.java index 112a531fa9..26bc1c172b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/PcodeCompile.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slgh_compile/PcodeCompile.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/SleighSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/SleighSymbol.java index c75010ba81..2bd4aba828 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/SleighSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcodeCPort/slghsymbol/SleighSymbol.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,7 +22,7 @@ import java.io.IOException; import ghidra.program.model.pcode.Encoder; import ghidra.sleigh.grammar.Location; -public class SleighSymbol implements Comparable { +public class SleighSymbol { @Override public String toString() { return name; @@ -85,11 +85,6 @@ public class SleighSymbol implements Comparable { encodeSleighSymbolHeader(encoder); } - @Override - public int compareTo(SleighSymbol o) { - return id - o.id; - } - public final Location location; public Location getLocation() { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Encoder.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Encoder.java index 06fb59019b..45f085bd33 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Encoder.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/Encoder.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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. + *

+ * 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. */ 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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedEncode.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedEncode.java index ca07bd44c4..d2fbf89ee1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedEncode.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PackedEncode.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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()); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlEncode.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlEncode.java index 798e725150..c74da2042e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlEncode.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/XmlEncode.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -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('>'); diff --git a/Ghidra/Processors/MCS96/data/languages/MCS96.sinc b/Ghidra/Processors/MCS96/data/languages/MCS96.sinc index cbc15f690d..b75988fac0 100644 --- a/Ghidra/Processors/MCS96/data/languages/MCS96.sinc +++ b/Ghidra/Processors/MCS96/data/languages/MCS96.sinc @@ -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; } diff --git a/Ghidra/Processors/PIC/data/languages/PIC24.sinc b/Ghidra/Processors/PIC/data/languages/PIC24.sinc index 1242db585b..c31fcbf81e 100644 --- a/Ghidra/Processors/PIC/data/languages/PIC24.sinc +++ b/Ghidra/Processors/PIC/data/languages/PIC24.sinc @@ -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 diff --git a/Ghidra/Processors/PowerPC/data/languages/SPE_APU.sinc b/Ghidra/Processors/PowerPC/data/languages/SPE_APU.sinc index cc07249e2a..e0ac98adf0 100644 --- a/Ghidra/Processors/PowerPC/data/languages/SPE_APU.sinc +++ b/Ghidra/Processors/PowerPC/data/languages/SPE_APU.sinc @@ -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; } diff --git a/Ghidra/Processors/PowerPC/data/languages/altivec.sinc b/Ghidra/Processors/PowerPC/data/languages/altivec.sinc index 7f4b339b42..720a3df089 100644 --- a/Ghidra/Processors/PowerPC/data/languages/altivec.sinc +++ b/Ghidra/Processors/PowerPC/data/languages/altivec.sinc @@ -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 {