diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc index f0f1502acd..3f00820f9e 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc @@ -820,10 +820,10 @@ void Architecture::parseLaneSizes(const Element *el) const List &list(el->getChildren()); List::const_iterator iter; - AllowedLanes allowedLanes; // Only allocate once + LanedRegister lanedRegister; // Only allocate once for(iter=list.begin();iter!=list.end();++iter) { - if (allowedLanes.restoreXml(*iter, this)) { - lanerecords.push_back(allowedLanes); + if (lanedRegister.restoreXml(*iter, this)) { + lanerecords.push_back(lanedRegister); } } lanerecords.sort(); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh index 62bb9d2d5b..04794affab 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh @@ -152,7 +152,7 @@ public: vector inst; ///< Registered p-code instructions UserOpManage userops; ///< Specifically registered user-defined p-code ops vector splitrecords; ///< registers that we would prefer to see split for this processor - list lanerecords; ///< Vector registers that have preferred lane sizes + list lanerecords; ///< Vector registers that have preferred lane sizes ActionDatabase allacts; ///< Actions that can be applied in this architecture bool loadersymbols_parsed; ///< True if loader symbols have been read #ifdef CPUI_STATISTICS diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index a81af0231a..cff9047f1c 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -16,6 +16,7 @@ #include "coreaction.hh" #include "condexe.hh" #include "double.hh" +#include "subflow.hh" /// \brief A stack equation struct StackEqn { @@ -494,6 +495,94 @@ int4 ActionStackPtrFlow::apply(Funcdata &data) return 0; } +/// \brief Try to divide a single Varnode into lanes +/// +/// Look for a CPUI_SUBPIECE op that takes the given Varnode as the input. If +/// the output size is an acceptable lane size, try to split data-flow through +/// this Varnode using this lane scheme. Try a split for every output size of such a +/// SUBPIECE op until one succeeds. +/// \param data is the function being transformed +/// \param vn is the given single Varnode +/// \param lanedRegister is acceptable set of lane sizes for the Varnode +bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister) + +{ + list::const_iterator iter = vn->beginDescend(); + LanedRegister checkedLanes; + while(iter != vn->endDescend()) { + PcodeOp *op = *iter; + ++iter; + if (op->code() != CPUI_SUBPIECE) continue; + int4 curSize = op->getOut()->getSize(); + if (lanedRegister.contains(curSize)) { + if (checkedLanes.contains(curSize)) continue; + checkedLanes.addSize(curSize); // Only check this scheme once + LaneDescription description(lanedRegister.getStorage().size,curSize); // Lane scheme dictated by curSize + int4 bytePos = (int4)(lanedRegister.getStorage().offset - vn->getOffset()); + if (lanedRegister.getStorage().space->isBigEndian()) { + bytePos = lanedRegister.getStorage().size - (bytePos + vn->getSize()); // Convert to significance order + } + if (!description.subset(bytePos,vn->getSize())) // Try to restrict lane scheme to actual Varnode + continue; + LaneDivide laneDivide(&data,op->getIn(0),description); + if (laneDivide.doTrace()) { + laneDivide.apply(); + count += 1; // Indicate a change was made + return true; + } + } + } + return false; +} + +/// \brief Search for and attempt to split Varnodes that match the given laned vector register +/// +/// \param data is the function being modified +/// \param lanedRegister is the given register and acceptable lane schemes +/// \param iter is an iterator to the first Varnode that matches the vector register +void ActionLaneDivide::processLane(Funcdata &data,const LanedRegister &lanedRegister,VarnodeLocSet::const_iterator iter) + +{ + int4 fullSize = lanedRegister.getStorage().size; + Address startAddress(lanedRegister.getStorage().getAddr()); + Address lastAddress(startAddress + (fullSize-1)); + VarnodeLocSet::const_iterator enditer = data.endLoc(); + while(iter != enditer) { + Varnode *vn = *iter; + ++iter; + if (lastAddress < vn->getAddr()) + break; + int4 diff = (int4)(vn->getOffset() - startAddress.getOffset()); + if (diff + vn->getSize() > fullSize) // Must be contained by full register + continue; + if (diff != 0 && diff != fullSize/2) // Must be all or half of full register + continue; + if (processVarnode(data,vn,lanedRegister)) { + // If changes were made, iterator may no longer be valid, generate a new one + iter = data.beginLoc(startAddress); + } + } +} + +int4 ActionLaneDivide::apply(Funcdata &data) + +{ + if (data.getArch()->lanerecords.empty()) + return 0; + const LanedRegister &lastRecord( data.getArch()->lanerecords.back() ); + Address lastAddress( lastRecord.getStorage().getAddr() + lastRecord.getStorage().size - 1); + list::const_iterator iter; + for(iter=data.getArch()->lanerecords.begin();iter!=data.getArch()->lanerecords.end();++iter) { + const LanedRegister &lanedRegister( *iter ); + VarnodeLocSet::const_iterator viter = data.beginLoc(lanedRegister.getStorage().getAddr()); + if (viter == data.endLoc()) break; + Varnode *vn = *viter; + if (lastAddress < vn->getAddr()) break; + processLane(data,lanedRegister,viter); + } + return 0; +} + int4 ActionSegmentize::apply(Funcdata &data) { @@ -4505,6 +4594,7 @@ void universal_action(Architecture *conf) // actmainloop->addAction( new ActionParamShiftStop("paramshift") ); actmainloop->addAction( new ActionRestrictLocal("localrecovery") ); // Do before dead code removed actmainloop->addAction( new ActionDeadCode("deadcode") ); + actmainloop->addAction( new ActionLaneDivide("base") ); actmainloop->addAction( new ActionDynamicMapping("dynamic") ); // Must come before restructurevarnode and infertypes actmainloop->addAction( new ActionRestructureVarnode("localrecovery") ); actmainloop->addAction( new ActionSpacebase("base") ); // Must come before infertypes and nonzeromask diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh index 07cc18900d..b466370c18 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.hh @@ -97,6 +97,24 @@ public: virtual int4 apply(Funcdata &data); }; +/// \brief Find Varnodes with a vectorized lane scheme and attempt to split the lanes +/// +/// The Architecture lists (vector) registers that may be used to perform parallelized operations +/// on \b lanes within the register. This action looks for these registers as Varnodes, determines +/// if a particular lane scheme makes sense in terms of the function's data-flow, and then +/// rewrites the data-flow so that the lanes become explicit Varnodes. +class ActionLaneDivide : public Action { + bool processVarnode(Funcdata &data,Varnode *vn,const LanedRegister &lanedRegister); + void processLane(Funcdata &data,const LanedRegister &lanedRegister,VarnodeLocSet::const_iterator iter); +public: + ActionLaneDivide(const string &g) : Action(rule_onceperfunc,"lanedivide",g) {} ///< Constructor + virtual Action *clone(const ActionGroupList &grouplist) const { + if (!grouplist.contains(getGroup())) return (Action *)0; + return new ActionLaneDivide(getGroup()); + } + virtual int4 apply(Funcdata &data); +}; + /// \brief Make sure pointers into segmented spaces have the correct form. /// /// Convert user-defined ops defined as segment p-code ops by a cspec tag into the internal CPUI_SEGMENTOP diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/transform.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/transform.cc index 8d0eef3190..e59b0ec5bf 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/transform.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/transform.cc @@ -58,6 +58,36 @@ LaneDescription::LaneDescription(int4 origSize,int4 lo,int4 hi) lanePosition[1] = lo; } +/// Given a subrange, specified as an offset into the whole and size, +/// throw out any lanes in \b this that aren't in the subrange, so that the +/// size of whole is the size of the subrange. If the subrange intersects partially +/// with any of the lanes, return \b false. +/// \param lsbOffset is the number of bytes to remove from the front of the description +/// \param size is the number of bytes in the subrange +/// \return \b true if \b this was successfully transformed to the subrange +bool LaneDescription::subset(int4 lsbOffset,int4 size) + +{ + if (lsbOffset == 0 && size == wholeSize) + return true; // subrange is the whole range + int4 firstLane = getBoundary(lsbOffset); + if (firstLane < 0) return false; + int4 lastLane = getBoundary(lsbOffset + size); + if (lastLane < 0) return false; + vector newLaneSize; + lanePosition.clear(); + int4 newPosition = 0; + for(int4 i=firstLane;i> sz; - if (sz < 0) return false; - sizes.push_back(sz); + if (sz < 0 || sz > 16) + throw LowlevelError("Bad lane size: " + value); + addSize(sz); } return true; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh index 02e09fff24..7667b60bf1 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/transform.hh @@ -85,16 +85,16 @@ public: }; /// \brief Describes a (register) storage location and the ways it might be split into lanes -class AllowedLanes { +class LanedRegister { VarnodeData storage; ///< Defining characteristics of the register - vector sizes; ///< Size of individual lane (in bytes) for each possible lane splitting + uint4 sizeBitMask; ///< A 1-bit for every permissible lane size public: - AllowedLanes(void) {} ///< Constructor for use with restoreXml + LanedRegister(void) { sizeBitMask = 0; } ///< Constructor bool restoreXml(const Element *el,const AddrSpaceManager *manage); ///< Restore object from XML stream const VarnodeData &getStorage(void) const { return storage; } ///< Get VarnodeData for storage - int4 numSplittings(void) const { return sizes.size(); } ///< Get the number of different lane splittings - int4 getBaseLaneSize(int4 i) const { return sizes[i]; } ///< Get the base lane size for the i-th splitting - bool operator<(const AllowedLanes &op2) const { return (storage < op2.storage); } ///< Compare based on VarnodeData + void addSize(int4 size) { sizeBitMask |= ((uint4)1 << size); } ///< Add a new \e size to the allowed list + bool contains(int4 size) const { return (((sizeBitMask >> size) & 1) != 0); } ///< Is \e size among the allowed lane sizes + bool operator<(const LanedRegister &op2) const { return (storage < op2.storage); } ///< Compare based on VarnodeData }; /// \brief Description of logical lanes within a \b big Varnode @@ -110,6 +110,7 @@ public: LaneDescription(const LaneDescription &op2); ///< Copy constructor LaneDescription(int4 origSize,int4 sz); ///< Construct uniform lanes LaneDescription(int4 origSize,int4 lo,int4 hi); ///< Construct two lanes of arbitrary size + bool subset(int4 lsbOffset,int4 size); ///< Trim \b this to a subset of the original lanes int4 getNumLanes(void) const { return laneSize.size(); } ///< Get the total number of lanes int4 getWholeSize(void) const { return wholeSize; } ///< Get the size of the region being split int4 getSize(int4 i) const { return laneSize[i]; } ///< Get the size of the i-th lane