/* ### * IP: GHIDRA * NOTE: Phi placement and renaming based on ACM journal articles * * 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. * See the License for the specific language governing permissions and * limitations under the License. */ /// \file heritage.hh /// \brief Utilities for building Static Single Assignment (SSA) form #ifndef __CPUI_HERITAGE__ #define __CPUI_HERITAGE__ #include "block.hh" /// Container holding the stack system for the renaming algorithm. Every disjoint address /// range (indexed by its initial address) maps to its own Varnode stack. typedef map > VariableStack; /// \brief Label for describing extent of address range that has been heritaged struct SizePass { int4 size; ///< Size of the range (in bytes) int4 pass; ///< Pass when the range was heritaged }; /// \brief Map object for keeping track of which address ranges have been heritaged /// /// We keep track of a fairly fine grained description of when each address range /// was entered in SSA form, referred to as \b heritaged or, for Varnode objects, /// no longer \b free. An address range is added using the add() method, which includes /// the particular pass when it was entered. The map can be queried using findPass() /// that informs the caller whether the address has been heritaged and if so in which pass. class LocationMap { public: /// Iterator into the main map typedef map::iterator iterator; private: map themap; ///< Heritaged addresses mapped to range size and pass number public: iterator add(Address addr,int4 size,int4 pass,int4 &intersect); ///< Mark new address as \b heritaged iterator find(const Address &addr); ///< Look up if/how given address was heritaged int4 findPass(const Address &addr) const; ///< Look up if/how given address was heritaged void erase(iterator iter) { themap.erase(iter); } ///< Remove a particular entry from the map iterator begin(void) { return themap.begin(); } ///< Get starting iterator over heritaged ranges iterator end(void) { return themap.end(); } ///< Get ending iterator over heritaged ranges void clear(void) { themap.clear(); } ///< Clear the map of heritaged ranges }; /// \brief Priority queue for the phi-node (MULTIEQUAL) placement algorithm /// /// A \e work-list for basic blocks used during phi-node placement. Implemented as /// a set of stacks with an associated priority. Blocks are placed in the \e queue /// with an associated \e priority (or depth) using the insert() method. The current /// highest priority block is retrieved with the extract() method. class PriorityQueue { vector > queue; ///< An array of \e stacks, indexed by priority int4 curdepth; ///< The current highest priority index with active blocks public: PriorityQueue(void) { curdepth = -2; } ///< Constructor void reset(int4 maxdepth); ///< Reset to an empty queue void insert(FlowBlock *bl,int4 depth); ///< Insert a block into the queue given its priority FlowBlock *extract(void); ///< Retrieve the highest priority block bool empty(void) const { return (curdepth==-1); } ///< Return \b true if \b this queue is empty }; class Funcdata; class FuncCallSpecs; /// \brief Information about heritage passes performed for a specific address space /// /// For a particular address space, this keeps track of: /// - how long to delay heritage /// - how long to delay dead code removal /// - whether dead code has been removed (for this space) /// - have warnings been issued class HeritageInfo { friend class Heritage; AddrSpace *space; ///< The address space \b this record describes int4 delay; ///< How many passes to delay heritage of this space int4 deadcodedelay; ///< How many passes to delay deadcode removal of this space int4 deadremoved; ///< >0 if Varnodes in this space have been eliminated bool loadGuardSearch; ///< \b true if the search for LOAD ops to guard has been performed bool warningissued; ///< \b true if warning issued previously bool hasCallPlaceholders; ///< \b true for the \e stack space, if stack placeholders have not been removed bool isHeritaged(void) const { return (space != (AddrSpace *)0); } ///< Return \b true if heritage is performed on this space void reset(void); ///< Reset the state public: HeritageInfo(AddrSpace *spc); ///< Constructor }; /// \brief Description of a LOAD operation that needs to be guarded /// /// Heritage maintains a list of CPUI_LOAD ops that reference the stack dynamically. These /// can potentially alias stack Varnodes, so we maintain what (possibly limited) information /// we known about the range of stack addresses that can be referenced. class LoadGuard { friend class Heritage; PcodeOp *op; ///< The LOAD op AddrSpace *spc; ///< The stack space being loaded from uintb pointerBase; ///< Base offset of the pointer uintb minimumOffset; ///< Minimum offset of the LOAD uintb maximumOffset; ///< Maximum offset of the LOAD int4 step; ///< Step of any access into this range (0=unknown) int4 analysisState; ///< 0=unanalyzed, 1=analyzed(partial result), 2=analyzed(full result) void establishRange(const ValueSetRead &valueSet); ///< Convert partial value set analysis into guard range void finalizeRange(const ValueSetRead &valueSet); ///< Convert value set analysis to final guard range /// \brief Set a new unanalyzed LOAD guard that initially guards everything /// /// \param o is the LOAD op /// \param s is the (stack) space it is loading from /// \param off is the base offset that is indexed from void set(PcodeOp *o,AddrSpace *s,uintb off) { op = o; spc = s; pointerBase=off; minimumOffset=0; maximumOffset=s->getHighest(); step=0; analysisState=0; } public: PcodeOp *getOp(void) const { return op; } ///< Get the PcodeOp being guarded uintb getMinimum(void) const { return minimumOffset; } ///< Get minimum offset of the guarded range uintb getMaximum(void) const { return maximumOffset; } ///< Get maximum offset of the guarded range int4 getStep(void) const { return step; } ///< Get the calculated step associated with the range (or 0) bool isGuarded(const Address &addr) const; ///< Does \b this guard apply to the given address bool isRangeLocked(void) const { return (analysisState == 2); } ///< Return \b true if the range is fully determined bool isValid(OpCode opc) const { return (!op->isDead() && op->code() == opc); } ///< Return \b true if the record still describes an active LOAD }; /// \brief Manage the construction of Static Single Assignment (SSA) form /// /// With a specific function (Funcdata), this class links the Varnode and /// PcodeOp objects into the formal data-flow graph structure, SSA form. /// The full structure can be built over multiple passes. In particular, /// this allows register data-flow to be analyzed first, and then stack /// locations can be discovered and promoted to first-class Varnodes in /// a second pass. /// /// Varnodes for which it is not known whether they are written to by a /// PcodeOp are referred to as \b free. The method heritage() performs /// a \e single \e pass of constructing SSA form, collecting any \e eligible /// free Varnodes for the pass and linking them in to the data-flow. A /// Varnode is considered eligible for a given pass generally based on its /// address space (see HeritageInfo), which is the main method for delaying /// linking for stack locations until they are all discovered. In /// principle a Varnode can be discovered very late and still get linked /// in on a subsequent pass. Linking causes Varnodes to gain new descendant /// PcodeOps, which has impact on dead code elimination (see LocationMap). /// /// The two big aspects of SSA construction are phi-node placement, performed /// by placeMultiequals(), and the \e renaming algorithm, performed by rename(). /// The various guard* methods are concerned with labeling analyzing /// data-flow across function calls, STORE, and LOAD operations. /// /// The phi-node placement algorithm is from (preprint?) /// "The Static Single Assignment Form and its Computation" /// by Gianfranco Bilardi and Keshav Pingali, July 22, 1999 /// /// The renaming algorithm taken from /// "Efficiently computing static single assignment form and the /// control dependence graph." /// R. Cytron, J. Ferrante, B. K. Rosen, M. N. Wegman, and F. K. Zadeck /// ACM Transactions on Programming Languages and Systems, /// 13(4):451-490, October 1991 class Heritage { /// Extra boolean properties on basic blocks for the Augmented Dominator Tree enum heritage_flags { boundary_node = 1, ///< Augmented Dominator Tree boundary node mark_node = 2, ///< Node has already been in queue merged_node = 4 ///< Node has already been merged }; /// \brief Node for depth-first traversal of stack references struct StackNode { enum { nonconstant_index = 1, multiequal = 2 }; Varnode *vn; ///< Varnode being traversed uintb offset; ///< Offset relative to base uint4 traversals; ///< What kind of operations has this pointer accumulated list::const_iterator iter; ///< Next PcodeOp to follow /// \brief Constructor /// \param v is the Varnode being visited /// \param o is the current offset from the base pointer /// \param trav indicates what configurations were seen along the path to this Varnode StackNode(Varnode *v,uintb o,uint4 trav) { vn = v; offset = o; iter = v->beginDescend(); traversals = trav; } }; Funcdata *fd; ///< The function \b this is controlling SSA construction LocationMap globaldisjoint; ///< Disjoint cover of every heritaged memory location LocationMap disjoint; ///< Disjoint cover of memory locations currently being heritaged vector > domchild; ///< Parent->child edges in dominator tree vector > augment; ///< Augmented edges vector flags; ///< Block properties for phi-node placement algorithm vector depth; ///< Dominator depth of individual blocks int4 maxdepth; ///< Maximum depth of the dominator tree int4 pass; ///< Current pass being executed PriorityQueue pq; ///< Priority queue for phi-node placement vector merge; ///< Calculate merge points (blocks containing phi-nodes) vector infolist; ///< Heritage status for individual address spaces list loadGuard; ///< List of LOAD operations that need to be guarded list storeGuard; ///< List of STORE operations taking an indexed pointer to the stack vector loadCopyOps; ///< List of COPY ops generated by load guards void clearInfoList(void); ///< Reset heritage status for all address spaces /// \brief Get the heritage status for the given address space HeritageInfo *getInfo(AddrSpace *spc) { return &(infolist[spc->getIndex()]); } /// \brief Get the heritage status for the given address space const HeritageInfo *getInfo(AddrSpace *spc) const { return &(infolist[spc->getIndex()]); } void clearStackPlaceholders(HeritageInfo *info); ///< Clear remaining stack placeholder LOADs on any call void splitJoinLevel(vector &lastcombo,vector &nextlev,JoinRecord *joinrec); void splitJoinRead(Varnode *vn,JoinRecord *joinrec); void splitJoinWrite(Varnode *vn,JoinRecord *joinrec); void floatExtensionRead(Varnode *vn,JoinRecord *joinrec); void floatExtensionWrite(Varnode *vn,JoinRecord *joinrec); void processJoins(void); void buildADT(void); ///< Build the augmented dominator tree void removeRevisitedMarkers(const vector &remove,const Address &addr,int4 size); int4 collect(Address addr,int4 size,vector &read,vector &write,vector &input,vector &remove) const; bool callOpIndirectEffect(const Address &addr,int4 size,PcodeOp *op) const; Varnode *normalizeReadSize(Varnode *vn,const Address &addr,int4 size); Varnode *normalizeWriteSize(Varnode *vn,const Address &addr,int4 size); Varnode *concatPieces(const vector &vnlist,PcodeOp *insertop,Varnode *finalvn); void splitPieces(const vector &vnlist,PcodeOp *insertop,const Address &addr,int4 size,Varnode *startvn); void findAddressForces(vector ©Sinks,vector &forces); void propagateCopyAway(PcodeOp *op); void handleNewLoadCopies(void); void analyzeNewLoadGuards(void); void generateLoadGuard(StackNode &node,PcodeOp *op,AddrSpace *spc); void generateStoreGuard(StackNode &node,PcodeOp *op,AddrSpace *spc); bool protectFreeStores(AddrSpace *spc,vector &freeStores); bool discoverIndexedStackPointers(AddrSpace *spc,vector &freeStores,bool checkFreeStores); void reprocessFreeStores(AddrSpace *spc,vector &freeStores); void guard(const Address &addr,int4 size,vector &read,vector &write,vector &inputvars); void guardInput(const Address &addr,int4 size,vector &input); void guardCallOverlappingInput(FuncCallSpecs *fc,const Address &addr,const Address &transAddr,int4 size); void guardCalls(uint4 fl,const Address &addr,int4 size,vector &write); void guardStores(const Address &addr,int4 size,vector &write); void guardLoads(uint4 fl,const Address &addr,int4 size,vector &write); void guardReturns(uint4 fl,const Address &addr,int4 size,vector &write); static void buildRefinement(vector &refine,const Address &addr,int4 size,const vector &vnlist); void splitByRefinement(Varnode *vn,const Address &addr,const vector &refine,vector &split); void refineRead(Varnode *vn,const Address &addr,const vector &refine,vector &newvn); void refineWrite(Varnode *vn,const Address &addr,const vector &refine,vector &newvn); void refineInput(Varnode *vn,const Address &addr,const vector &refine,vector &newvn); void remove13Refinement(vector &refine); bool refinement(const Address &addr,int4 size,const vector &readvars,const vector &writevars,const vector &inputvars); void visitIncr(FlowBlock *qnode,FlowBlock *vnode); void calcMultiequals(const vector &write); void renameRecurse(BlockBasic *bl,VariableStack &varstack); void bumpDeadcodeDelay(Varnode *vn); void placeMultiequals(void); void rename(void); public: Heritage(Funcdata *data); ///< Constructor int4 getPass(void) const { return pass; } ///< Get overall count of heritage passes /// \brief Get the pass number when the given address was heritaged /// /// \param addr is the given address /// \return the pass number or -1 if the address has not been heritaged int4 heritagePass(const Address &addr) const { return globaldisjoint.findPass(addr); } int4 numHeritagePasses(AddrSpace *spc) const; void seenDeadCode(AddrSpace *spc); ///< Inform system of dead code removal in given space int4 getDeadCodeDelay(AddrSpace *spc) const; ///< Get pass delay for heritaging the given space void setDeadCodeDelay(AddrSpace *spc,int4 delay); ///< Set delay for a specific space bool deadRemovalAllowed(AddrSpace *spc) const; ///< Return \b true if it is \e safe to remove dead code bool deadRemovalAllowedSeen(AddrSpace *spc); void buildInfoList(void); ///< Initialize information for each space void forceRestructure(void) { maxdepth = -1; } ///< Force regeneration of basic block structures void clear(void); ///< Reset all analysis of heritage void heritage(void); ///< Perform one pass of heritage const list &getLoadGuards(void) const { return loadGuard; } ///< Get list of LOAD ops that are guarded const list &getStoreGuards(void) const { return storeGuard; } ///< Get list of STORE ops that are guarded const LoadGuard *getStoreGuard(PcodeOp *op) const; ///< Get LoadGuard record associated with given PcodeOp }; #endif