/* ###
 * IP: GHIDRA
 *
 * 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 jumptable.hh
/// \brief Classes to support jump-tables and their recovery

#ifndef __JUMPTABLE_HH__
#define __JUMPTABLE_HH__

#include "emulateutil.hh"
#include "rangeutil.hh"

namespace ghidra {

class EmulateFunction;

extern AttributeId ATTRIB_LABEL;	///< Marshaling attribute "label"
extern AttributeId ATTRIB_NUM;		///< Marshaling attribute "num"

extern ElementId ELEM_BASICOVERRIDE;	///< Marshaling element \<basicoverride>
extern ElementId ELEM_DEST;		///< Marshaling element \<dest>
extern ElementId ELEM_JUMPTABLE;	///< Marshaling element \<jumptable>
extern ElementId ELEM_LOADTABLE;	///< Marshaling element \<loadtable>
extern ElementId ELEM_NORMADDR;		///< Marshaling element \<normaddr>
extern ElementId ELEM_NORMHASH;		///< Marshaling element \<normhash>
extern ElementId ELEM_STARTVAL;		///< Marshaling element \<startval>

/// \brief Exception thrown for a thunk mechanism that looks like a jump-table
struct JumptableThunkError : public LowlevelError {
  JumptableThunkError(const string &s) : LowlevelError(s) {}	///< Construct with an explanatory string
};

/// \brief Exception thrown is there are no legal flows to a switch
struct JumptableNotReachableError : public LowlevelError {
  JumptableNotReachableError(const string &s) : LowlevelError(s) {}	///< Constructor
};

/// \brief A description where and how data was loaded from memory
///
/// This is a generic table description, giving the starting address
/// of the table, the size of an entry, and number of entries.
class LoadTable {
  friend class EmulateFunction;
  Address addr;			///< Starting address of table
  int4 size;			///< Size of table entry
  int4 num;			///< Number of entries in table;
public:
  LoadTable(void) {}		// Constructor for use with decode
  LoadTable(const Address &ad,int4 sz) { addr = ad, size = sz; num = 1; }	///< Constructor for a single entry table
  LoadTable(const Address &ad,int4 sz,int4 nm) { addr = ad; size = sz; num = nm; }	///< Construct a full table
  bool operator<(const LoadTable &op2) const { return (addr < op2.addr); }	///< Compare \b this with another table by address
  void encode(Encoder &encoder) const;				///< Encode a description of \b this as an \<loadtable> element
  void decode(Decoder &decoder);				///< Decode \b this table from a \<loadtable> element
  static void collapseTable(vector<LoadTable> &table);		///< Collapse a sequence of table descriptions
};

/// \brief All paths from a (putative) switch variable to the CPUI_BRANCHIND
///
/// This is a container for intersecting paths during the construction of a
/// JumpModel.  It contains every PcodeOp from some starting Varnode through
/// all paths to a specific BRANCHIND.  The paths can split and rejoin. This also
/// keeps track of Varnodes that are present on \e all paths, as these are the
/// potential switch variables for the model.
class PathMeld {

  /// \brief A PcodeOp in the path set associated with the last Varnode in the intersection
  ///
  /// This links a PcodeOp to the point where the flow path to it split from common path
  struct RootedOp {
    PcodeOp *op;	///< An op in the container
    int4 rootVn;	///< The index, within commonVn, of the Varnode at the split point
    RootedOp(PcodeOp *o,int4 root) { op = o; rootVn = root; }	///< Constructor
  };
  vector<Varnode *> commonVn;		///< Varnodes in common with all paths
  vector<RootedOp> opMeld;		///< All the ops for the melded paths
  void internalIntersect(vector<int4> &parentMap);
  int4 meldOps(const vector<PcodeOpNode> &path,int4 cutOff,const vector<int4> &parentMap);
  void truncatePaths(int4 cutPoint);
public:
  void set(const PathMeld &op2);	///< Copy paths from another container
  void set(const vector<PcodeOpNode> &path);	///< Initialize \b this to be a single path
  void set(PcodeOp *op,Varnode *vn);	///< Initialize \b this container to a single node "path"
  void append(const PathMeld &op2);	///< Append a new set of paths to \b this set of paths
  void clear(void);			///< Clear \b this to be an empty container
  void meld(vector<PcodeOpNode> &path);	///< Meld a new path into \b this container
  void markPaths(bool val,int4 startVarnode);			///< Mark PcodeOps paths from the given start
  int4 numCommonVarnode(void) const { return commonVn.size(); }	///< Return the number of Varnodes common to all paths
  int4 numOps(void) const { return opMeld.size(); }		///< Return the number of PcodeOps across all paths
  Varnode *getVarnode(int4 i) const { return commonVn[i]; }	///< Get the i-th common Varnode
  Varnode *getOpParent(int4 i) const { return commonVn[ opMeld[i].rootVn ]; }	///< Get the split-point for the i-th PcodeOp
  PcodeOp *getOp(int4 i) const { return opMeld[i].op; }		///< Get the i-th PcodeOp
  PcodeOp *getEarliestOp(int4 pos) const;		///< Find \e earliest PcodeOp that has a specific common Varnode as input
  bool empty(void) const { return commonVn.empty(); }	///< Return \b true if \b this container holds no paths
};

/// \brief A light-weight emulator to calculate switch targets from switch variables
///
/// We assume we only have to store memory state for individual Varnodes and that dynamic
/// LOADs are resolved from the LoadImage. BRANCH and CBRANCH emulation will fail, there can
/// only be one execution path, although there can be multiple data-flow paths.
class EmulateFunction : public EmulatePcodeOp {
  Funcdata *fd;				///< The function being emulated
  map<Varnode *,uintb> varnodeMap;	///< Light-weight memory state based on Varnodes
  bool collectloads;			///< Set to \b true if the emulator collects individual LOAD addresses
  vector<LoadTable> loadpoints;		///< The set of collected LOAD records
  virtual void executeLoad(void);
  virtual void executeBranch(void);
  virtual void executeBranchind(void);
  virtual void executeCall(void);
  virtual void executeCallind(void);
  virtual void executeCallother(void);
  virtual void fallthruOp(void);
public:
  EmulateFunction(Funcdata *f);		///< Constructor
  void setLoadCollect(bool val) { collectloads = val; }	///< Set whether we collect LOAD information
  virtual void setExecuteAddress(const Address &addr);
  virtual uintb getVarnodeValue(Varnode *vn) const;
  virtual void setVarnodeValue(Varnode *vn,uintb val);
  uintb emulatePath(uintb val,const PathMeld &pathMeld,PcodeOp *startop,Varnode *startvn);
  void collectLoadPoints(vector<LoadTable> &res) const;	///< Recover any LOAD table descriptions
};

class FlowInfo;
class JumpTable;

/// \brief A (putative) switch variable Varnode and a constraint imposed by a CBRANCH
///
/// The record constrains a specific Varnode.  If the associated CBRANCH is followed
/// along the path that reaches the switch's BRANCHIND, then we have an explicit
/// description of the possible values the Varnode can hold.
class GuardRecord {
  PcodeOp *cbranch;		///< PcodeOp CBRANCH the branches around the switch
  PcodeOp *readOp;		///< The immediate PcodeOp causing the restriction
  Varnode *vn;			///< The Varnode being restricted
  Varnode *baseVn;		///< Value being (quasi)copied to the Varnode
  int4 indpath;			///< Specific CBRANCH path going to the switch
  int4 bitsPreserved;		///< Number of bits copied (all other bits are zero)
  CircleRange range;		///< Range of values causing the CBRANCH to take the path to the switch
  bool unrolled;		///< \b true if guarding CBRANCH is duplicated across multiple blocks
public:
  GuardRecord(PcodeOp *bOp,PcodeOp *rOp,int4 path,const CircleRange &rng,Varnode *v,bool unr=false);	///< Constructor
  bool isUnrolled(void) const { return unrolled; }	///< Is \b this guard duplicated across multiple blocks
  PcodeOp *getBranch(void) const { return cbranch; }	///< Get the CBRANCH associated with \b this guard
  PcodeOp *getReadOp(void) const { return readOp; }	///< Get the PcodeOp immediately causing the restriction
  int4 getPath(void) const { return indpath; }		///< Get the specific path index going towards the switch
  const CircleRange &getRange(void) const { return range; }	///< Get the range of values causing the switch path to be taken
  void clear(void) { cbranch = (PcodeOp *)0; }		///< Mark \b this guard as unused
  int4 valueMatch(Varnode *vn2,Varnode *baseVn2,int4 bitsPreserved2) const;
  static int4 oneOffMatch(PcodeOp *op1,PcodeOp *op2);
  static Varnode *quasiCopy(Varnode *vn,int4 &bitsPreserved);
};

/// \brief An iterator over values a switch variable can take
///
/// This iterator is intended to provide the start value for emulation
/// of a jump-table model to obtain the associated jump-table destination.
/// Each value can be associated with a starting Varnode and PcodeOp in
/// the function being emulated, via getStartVarnode() and getStartOp().
class JumpValues {
public:
  virtual ~JumpValues(void) {}
  virtual void truncate(int4 nm)=0;	///< Truncate the number of values to the given number
  virtual uintb getSize(void) const=0;	///< Return the number of values the variables can take
  virtual bool contains(uintb val) const=0;	///< Return \b true if the given value is in the set of possible values

  /// \brief Initialize \b this for iterating over the set of possible values
  ///
  /// \return \b true if there are any values to iterate over
  virtual bool initializeForReading(void) const=0;

  virtual bool next(void) const=0;		///< Advance the iterator, return \b true if there is another value
  virtual uintb getValue(void) const=0;		///< Get the current value
  virtual Varnode *getStartVarnode(void) const=0;	///< Get the Varnode associated with the current value
  virtual PcodeOp *getStartOp(void) const=0;		///< Get the PcodeOp associated with the current value
  virtual bool isReversible(void) const=0;	///< Return \b true if the current value can be reversed to get a label
  virtual JumpValues *clone(void) const=0;	///< Clone \b this iterator
};

/// \brief single entry switch variable that can take a range of values
class JumpValuesRange : public JumpValues {
protected:
  CircleRange range;		///< Acceptable range of values for the normalized switch variable
  Varnode *normqvn;		///< Varnode representing the normalized switch variable
  PcodeOp *startop;		///< First PcodeOp in the jump-table calculation
  mutable uintb curval;		///< The current value pointed to be the iterator
public:
  void setRange(const CircleRange &rng) { range = rng; }	///< Set the range of values explicitly
  void setStartVn(Varnode *vn) { normqvn = vn; }		///< Set the normalized switch Varnode explicitly
  void setStartOp(PcodeOp *op) { startop = op; }		///< Set the starting PcodeOp explicitly
  virtual void truncate(int4 nm);
  virtual uintb getSize(void) const;
  virtual bool contains(uintb val) const;
  virtual bool initializeForReading(void) const;
  virtual bool next(void) const;
  virtual uintb getValue(void) const;
  virtual Varnode *getStartVarnode(void) const;
  virtual PcodeOp *getStartOp(void) const;
  virtual bool isReversible(void) const { return true; }
  virtual JumpValues *clone(void) const;
};

/// \brief A jump-table starting range with two possible execution paths
///
/// This extends the basic JumpValuesRange having a single entry switch variable and
/// adds a second entry point that takes only a single value. This value comes last in the iteration.
class JumpValuesRangeDefault : public JumpValuesRange {
  uintb extravalue;		///< The extra value
  Varnode *extravn;		///< The starting Varnode associated with the extra value
  PcodeOp *extraop;		///< The starting PcodeOp associated with the extra value
  mutable bool lastvalue;	///< \b true if the extra value has been visited by the iterator
public:
  void setExtraValue(uintb val) { extravalue = val; }	///< Set the extra value explicitly
  void setDefaultVn(Varnode *vn) { extravn = vn; }	///< Set the associated start Varnode
  void setDefaultOp(PcodeOp *op) { extraop = op; }	///< Set the associated start PcodeOp
  virtual uintb getSize(void) const;
  virtual bool contains(uintb val) const;
  virtual bool initializeForReading(void) const;
  virtual bool next(void) const;
  virtual Varnode *getStartVarnode(void) const;
  virtual PcodeOp *getStartOp(void) const;
  virtual bool isReversible(void) const { return !lastvalue; }	// The -extravalue- is not reversible
  virtual JumpValues *clone(void) const;
};

/// \brief A jump-table execution model
///
/// This class holds details of the model and recovers these details in various stages.
/// The model concepts include:
///   - Address Table, the set of destination addresses the jump-table can produce.
///   - Normalized Switch Variable, the Varnode with the most restricted set of values used
///       by the model to produce the destination addresses.
///   - Unnormalized Switch Variable, the Varnode being switched on, as seen in the decompiler output.
///   - Case labels, switch variable values associated with specific destination addresses.
///   - Guards, CBRANCH ops that enforce the normalized switch variable's value range.
class JumpModel {
protected:
  JumpTable *jumptable;		///< The jump-table that is building \b this model
public:
  JumpModel(JumpTable *jt) { jumptable = jt; }	///< Construct given a parent jump-table
  virtual ~JumpModel(void) {}			///< Destructor
  virtual bool isOverride(void) const=0;	///< Return \b true if \b this model was manually overridden
  virtual int4 getTableSize(void) const=0;	///< Return the number of entries in the address table

  /// \brief Attempt to recover details of the model, given a specific BRANCHIND
  ///
  /// This generally recovers the normalized switch variable and any guards.
  /// \param fd is the function containing the switch
  /// \param indop is the given BRANCHIND
  /// \param matchsize is the expected number of address table entries to recover, or 0 for no expectation
  /// \param maxtablesize is maximum number of address table entries to allow in the model
  /// \return \b true if details of the model were successfully recovered
  virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize)=0;

  /// \brief Construct the explicit list of target addresses (the Address Table) from \b this model
  ///
  /// The addresses produced all come from the BRANCHIND and may not be deduped. Alternate guard
  /// destinations are not yet included.
  /// \param fd is the function containing the switch
  /// \param indop is the root BRANCHIND of the switch
  /// \param addresstable will hold the list of Addresses
  /// \param loadpoints if non-null will hold LOAD table information used by the model
  virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const=0;

  /// \brief Recover the unnormalized switch variable
  ///
  /// The normalized switch variable must already be recovered. The amount of normalization between
  /// the two switch variables can be restricted.
  /// \param maxaddsub is a restriction on arithmetic operations
  /// \param maxleftright is a restriction on shift operations
  /// \param maxext is a restriction on extension operations
  virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext)=0;

  /// \brief Recover \e case labels associated with the Address table
  ///
  /// The unnormalized switch variable must already be recovered.  Values that the normalized
  /// switch value can hold or walked back to obtain the value that the unnormalized switch
  /// variable would hold. Labels are returned in the order provided by normalized switch
  /// variable iterator JumpValues.
  /// \param fd is the function containing the switch
  /// \param addresstable is the address table (used to label code blocks with bad or missing labels)
  /// \param label will hold recovered labels in JumpValues order
  /// \param orig is the JumpModel to use for the JumpValues iterator
  virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const=0;

  /// \brief Do normalization of the given switch specific to \b this model.
  ///
  /// The PcodeOp machinery is removed so it looks like the CPUI_BRANCHIND simply takes the
  /// switch variable as an input Varnode and automatically interprets its values to reach
  /// the correct destination.
  /// \param fd is the function containing the switch
  /// \param indop is the given switch as a CPUI_BRANCHIND
  /// \return the Varnode holding the final unnormalized switch variable
  virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop)=0;

  /// \brief Eliminate any \e guard code involved in computing the switch destination
  ///
  /// We now think of the BRANCHIND as encompassing any guard function.
  /// \param fd is the function containing the switch
  /// \param jump is the JumpTable owning \b this model.
  virtual bool foldInGuards(Funcdata *fd,JumpTable *jump)=0;

  /// \brief Perform a sanity check on recovered addresses
  ///
  /// Individual addresses are checked against the function or its program to determine
  /// if they are reasonable. This method can optionally remove addresses from the table.
  /// If it does so, the underlying model is changed to reflect the removal.
  /// \param fd is the function containing the switch
  /// \param indop is the root BRANCHIND of the switch
  /// \param addresstable is the list of recovered Addresses, which may be modified
  /// \return \b true if there are (at least some) reasonable addresses in the table
  virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable)=0;

  virtual JumpModel *clone(JumpTable *jt) const=0;	///< Clone \b this model
  virtual void clear(void) {}				///< Clear any non-permanent aspects of the model
  virtual void encode(Encoder &encoder) const {} 	///< Encode this model to a stream
  virtual void decode(Decoder &decoder) {}		///< Decode \b this model from a stream
};

/// \brief A trivial jump-table model, where the BRANCHIND input Varnode is the switch variable
///
/// This class treats the input Varnode to the BRANCHIND as the switch variable, and recovers
/// its possible values from the existing block structure. This is used when the flow following
/// fork recovers destination addresses, but the switch normalization action is unable to recover
/// the model.
class JumpModelTrivial : public JumpModel {
  uint4 size;			///< Number of addresses in the table as reported by the JumpTable
public:
  JumpModelTrivial(JumpTable *jt) : JumpModel(jt) { size = 0; }	///< Construct given a parent JumpTable
  virtual bool isOverride(void) const { return false; }
  virtual int4 getTableSize(void) const { return size; }
  virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize);
  virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
  virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {}
  virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
  virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop) { return (Varnode *)0; }
  virtual bool foldInGuards(Funcdata *fd,JumpTable *jump) { return false; }
  virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable) { return true; }
  virtual JumpModel *clone(JumpTable *jt) const;
};

/// \brief The basic switch model
///
/// This is the most common model:
///   - A straight-line calculation from switch variable to BRANCHIND
///   - The switch variable is bounded by one or more \e guards that branch around the BRANCHIND
///   - The unnormalized switch variable is recovered from the normalized variable through some basic transforms
class JumpBasic : public JumpModel {
protected:
  JumpValuesRange *jrange;		///< Range of values for the (normalized) switch variable
  PathMeld pathMeld;			///< Set of PcodeOps and Varnodes producing the final target addresses
  vector<GuardRecord> selectguards;	///< Any guards associated with \b model
  int4 varnodeIndex;			///< Position of the normalized switch Varnode within PathMeld
  Varnode *normalvn;			///< Normalized switch Varnode
  Varnode *switchvn;			///< Unnormalized switch Varnode
  static bool isprune(Varnode *vn);	///< Do we prune in here in our depth-first search for the normalized switch variable
  static bool ispoint(Varnode *vn);	///< Is it possible for the given Varnode to be a switch variable?
  static int4 getStride(Varnode *vn);	///< Get the step/stride associated with the Varnode
  static uintb backup2Switch(Funcdata *fd,uintb output,Varnode *outvn,Varnode *invn);
  static uintb getMaxValue(Varnode *vn);	///< Get maximum value associated with the given Varnode
  void findDeterminingVarnodes(PcodeOp *op,int4 slot);
  void analyzeGuards(BlockBasic *bl,int4 pathout);
  void calcRange(Varnode *vn,CircleRange &rng) const;
  void findSmallestNormal(uint4 matchsize);
  void findNormalized(Funcdata *fd,BlockBasic *rootbl,int4 pathout,uint4 matchsize,uint4 maxtablesize);
  void markFoldableGuards();
  void markModel(bool val);		///< Mark (or unmark) all PcodeOps involved in the model
  bool flowsOnlyToModel(Varnode *vn,PcodeOp *trailOp);	///< Check if the given Varnode flows to anything other than \b this model
  bool checkCommonCbranch(vector<Varnode *> &varArray,BlockBasic *bl);	///< Check that all incoming blocks end with a CBRANCH
  void checkUnrolledGuard(BlockBasic *bl,int4 maxpullback,bool usenzmask);

  /// \brief Eliminate the given guard to \b this switch
  ///
  /// We \e disarm the guard instructions by making the guard condition
  /// always \b false.  If the simplification removes the unusable branches,
  /// we are left with only one path through the switch.
  /// \param fd is the function containing the switch
  /// \param guard is a description of the particular guard mechanism
  /// \param jump is the JumpTable owning \b this model
  /// \return \b true if a change was made to data-flow
  virtual bool foldInOneGuard(Funcdata *fd,GuardRecord &guard,JumpTable *jump);
public:
  JumpBasic(JumpTable *jt) : JumpModel(jt) { jrange = (JumpValuesRange *)0; }	///< Construct given a parent JumpTable
  const PathMeld &getPathMeld(void) const { return pathMeld; }		///< Get the possible of paths to the switch
  const JumpValuesRange *getValueRange(void) const { return jrange; }	///< Get the normalized value iterator
  virtual ~JumpBasic(void);
  virtual bool isOverride(void) const { return false; }
  virtual int4 getTableSize(void) const { return jrange->getSize(); }
  virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize);
  virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
  virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext);
  virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
  virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop);
  virtual bool foldInGuards(Funcdata *fd,JumpTable *jump);
  virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable);
  virtual JumpModel *clone(JumpTable *jt) const;
  virtual void clear(void);
};

/// \brief A basic jump-table model with an added default address path
///
/// This model expects two paths to the switch, 1 from a default value, 1 from the other values that hit the switch
/// If A is the guarding control-flow block, C is the block setting the default value, and S the switch block itself,
/// We expect one of the following situations:
///   - A -> C or S  and  C -> S
///   - A -> C or D  and  C -> S  D -> S
///   - C -> S and S -> A   A -> S or "out of loop", i.e. S is in a loop, and the guard block doubles as the loop condition
///
/// This builds on the analysis performed for JumpBasic, which fails because there are too many paths
/// to the BRANCHIND, preventing the guards from being interpreted properly.  This class expects to reuse
/// the PathMeld calculation from JumpBasic.
class JumpBasic2 : public JumpBasic {
  Varnode *extravn;		///< The extra Varnode holding the default value
  PathMeld origPathMeld;	///< The set of paths that produce non-default addresses
  bool checkNormalDominance(void) const;
  virtual bool foldInOneGuard(Funcdata *fd,GuardRecord &guard,JumpTable *jump);
public:
  JumpBasic2(JumpTable *jt) : JumpBasic(jt) {}	///< Constructor
  void initializeStart(const PathMeld &pMeld);	///< Pass in the prior PathMeld calculation
  virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize);
  virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext);
  virtual JumpModel *clone(JumpTable *jt) const;
  virtual void clear(void);
};

/// \brief A basic jump-table model incorporating manual override information
///
/// The list of potential target addresses produced by the BRANCHIND is not recovered by \b this
/// model, but must provided explicitly via setAddresses().
/// The model tries to repurpose some of the analysis that JumpBasic does to recover the switch variable.
/// But it will revert to the trivial model if it can't find a suitable switch variable.
class JumpBasicOverride : public JumpBasic {
  set<Address> adset;		///< Absolute address table (manually specified)
  vector<uintb> values;		///< Normalized switch variable values associated with addresses
  vector<Address> addrtable;	///< Address associated with each value
  uintb startingvalue;		///< Possible start for guessing values that match addresses
  Address normaddress;		///< Dynamic info for recovering normalized switch variable
  uint8 hash;			///< if (hash==0) there is no normalized switch (use trivial model)
  bool istrivial;		///< \b true if we use a trivial value model
  int4 findStartOp(Varnode *vn);
  int4 trialNorm(Funcdata *fd,Varnode *trialvn,uint4 tolerance);
  void setupTrivial(void);
  Varnode *findLikelyNorm(void);
  void clearCopySpecific(void);
public:
  JumpBasicOverride(JumpTable *jt);		///< Constructor
  void setAddresses(const vector<Address> &adtable);	///< Manually set the address table for \b this model
  void setNorm(const Address &addr,uintb h) { normaddress = addr; hash = h; }	///< Set the normalized switch variable
  void setStartingValue(uintb val) { startingvalue = val; }		///< Set the starting value for the normalized range
  virtual bool isOverride(void) const { return true; }
  virtual int4 getTableSize(void) const { return addrtable.size(); }
  virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize);
  virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
  // findUnnormalized inherited from JumpBasic
  virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
  // foldInNormalization inherited from JumpBasic
  virtual bool foldInGuards(Funcdata *fd,JumpTable *jump) { return false; }
  virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable) { return true; }
  virtual JumpModel *clone(JumpTable *jt) const;
  virtual void clear(void);
  virtual void encode(Encoder &encoder) const;
  virtual void decode(Decoder &decoder);
};

class JumpAssistOp;

/// \brief A jump-table model assisted by pseudo-op directives in the code
///
/// This model looks for a special \e jumpassist pseudo-op near the branch site, which contains
/// p-code models describing how to parse a jump-table for case labels and addresses.
/// It views the switch table calculation as a two-stage process:
///    - case2index:    convert the switchvar to an index into a table
///    - index2address: convert the index to an address
///
/// The pseudo-op holds:
///    - the table address, size (number of indices)
///    - exemplar p-code for inverting the case2index part of the calculation
///    - exemplar p-code for calculating index2address
class JumpAssisted : public JumpModel {
  PcodeOp *assistOp;		///< The \e jumpassist PcodeOp
  JumpAssistOp *userop;		///< The \e jumpassist p-code models
  int4 sizeIndices;		///< Total number of indices in the table (not including the defaultaddress)
  Varnode *switchvn;		///< The switch variable
public:
  JumpAssisted(JumpTable *jt) : JumpModel(jt) { assistOp = (PcodeOp *)0; switchvn = (Varnode *)0; sizeIndices=0; }	///< Constructor
//  virtual ~JumpAssisted(void);
  virtual bool isOverride(void) const { return false; }
  virtual int4 getTableSize(void) const { return sizeIndices+1; }
  virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize);
  virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
  virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {}
  virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
  virtual Varnode *foldInNormalization(Funcdata *fd,PcodeOp *indop);
  virtual bool foldInGuards(Funcdata *fd,JumpTable *jump);
  virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable) { return true; }
  virtual JumpModel *clone(JumpTable *jt) const;
  virtual void clear(void) { assistOp = (PcodeOp *)0; switchvn = (Varnode *)0; }
};

/// \brief A map from values to control-flow targets within a function
///
/// A JumpTable is attached to a specific CPUI_BRANCHIND and encapsulates all
/// the information necessary to model the indirect jump as a \e switch statement.
/// It knows how to map from specific switch variable values to the destination
/// \e case block and how to label the value.
class JumpTable {
  /// \brief An address table index and its corresponding out-edge
  struct IndexPair {
    int4 blockPosition;				///< Out-edge index for the basic-block
    int4 addressIndex;				///< Index of address targeting the basic-block
    IndexPair(int4 pos,int4 index) { blockPosition = pos; addressIndex = index; }	///< Constructor
    bool operator<(const IndexPair &op2) const;	///< Compare by position then by index
    static bool compareByPosition(const IndexPair &op1,const IndexPair &op2);	///< Compare just by position
  };
  Architecture *glb;		///< Architecture under which this jump-table operates
  JumpModel *jmodel;		///< Current model of how the jump table is implemented in code
  JumpModel *origmodel;		///< Initial jump table model, which may be incomplete
  vector<Address> addresstable; ///< Raw addresses in the jump-table
  vector<IndexPair> block2addr;	///< Map from basic-blocks to address table index
  vector<uintb> label;		///< The case label for each explicit target
  vector<LoadTable> loadpoints;	///< Any recovered in-memory data for the jump-table
  Address opaddress;		///< Absolute address of the BRANCHIND jump
  PcodeOp *indirect;		///< CPUI_BRANCHIND linked to \b this jump-table
  uintb switchVarConsume;	///< Bits of the switch variable being consumed
  int4 defaultBlock;		///< The out-edge corresponding to the \e default switch destination (-1 = undefined)
  int4 lastBlock;		///< Block out-edge corresponding to last entry in the address table
  uint4 maxaddsub;		///< Maximum ADDs or SUBs to normalize
  uint4 maxleftright;		///< Maximum shifts to normalize
  uint4 maxext;			///< Maximum extensions to normalize
  int4 recoverystage;		///< 0=no stages recovered, 1=additional stage needed, 2=complete
  bool collectloads;		///< Set to \b true if information about in-memory model data is/should be collected
  void recoverModel(Funcdata *fd);	///< Attempt recovery of the jump-table model
  void trivialSwitchOver(void);	///< Switch \b this table over to a trivial model
  void sanityCheck(Funcdata *fd);	///< Perform sanity check on recovered address targets
  int4 block2Position(const FlowBlock *bl) const;	///< Convert a basic-block to an out-edge index from the switch.
  static bool isReachable(PcodeOp *op);	///< Check if the given PcodeOp still seems reachable in its function
public:
  JumpTable(Architecture *g,Address ad=Address());	///< Constructor
  JumpTable(const JumpTable *op2);			///< Copy constructor
  ~JumpTable(void);					///< Destructor
  bool isRecovered(void) const { return !addresstable.empty(); }	///< Return \b true if a model has been recovered
  bool isLabelled(void) const { return !label.empty(); }		///< Return \b true if \e case labels are computed
  bool isOverride(void) const;				///< Return \b true if \b this table was manually overridden
  bool isPossibleMultistage(void) const { return (addresstable.size()==1); }	///< Return \b true if this could be multi-staged
  int4 getStage(void) const { return recoverystage; }	///< Return what stage of recovery this jump-table is in.
  int4 numEntries(void) const { return addresstable.size(); }	///< Return the size of the address table for \b this jump-table
  uintb getSwitchVarConsume(void) const { return switchVarConsume; }	///< Get bits of switch variable consumed by \b this table
  int4 getDefaultBlock(void) const { return defaultBlock; }	///< Get the out-edge corresponding to the \e default switch destination
  const Address &getOpAddress(void) const { return opaddress; }	///< Get the address of the BRANCHIND for the switch
  PcodeOp *getIndirectOp(void) const { return indirect; }	///< Get the BRANCHIND PcodeOp
  void setIndirectOp(PcodeOp *ind) { opaddress = ind->getAddr(); indirect = ind; }	///< Set the BRANCHIND PcodeOp
  void setNormMax(uint4 maddsub,uint4 mleftright,uint4 mext) {
    maxaddsub = maddsub; maxleftright = mleftright; maxext = mext; }	///< Set the switch variable normalization model restrictions
  void setOverride(const vector<Address> &addrtable,const Address &naddr,uintb h,uintb sv);
  int4 numIndicesByBlock(const FlowBlock *bl) const;
  int4 getIndexByBlock(const FlowBlock *bl,int4 i) const;
  Address getAddressByIndex(int4 i) const { return addresstable[i]; }	///< Get the i-th address table entry
  void setLastAsMostCommon(void);		///< Set the most common jump-table target to be the last address in the table
  void setDefaultBlock(int4 bl) { defaultBlock = bl; }		///< Set out-edge of the switch destination considered to be \e default
  void setLoadCollect(bool val) { collectloads = val; }		///< Set whether LOAD records should be collected
  void addBlockToSwitch(BlockBasic *bl,uintb lab);		///< Force a given basic-block to be a switch destination
  void switchOver(const FlowInfo &flow);				///< Convert absolute addresses to block indices
  uintb getLabelByIndex(int4 index) const { return label[index]; }	///< Given a \e case index, get its label
  void foldInNormalization(Funcdata *fd);		///< Hide the normalization code for the switch
  bool foldInGuards(Funcdata *fd) { return jmodel->foldInGuards(fd,this); }	///< Hide any guard code for \b this switch
  void recoverAddresses(Funcdata *fd);		///< Recover the raw jump-table addresses (the address table)
  void recoverMultistage(Funcdata *fd);		///< Recover jump-table addresses keeping track of a possible previous stage
  bool recoverLabels(Funcdata *fd);		///< Recover the case labels for \b this jump-table
  bool checkForMultistage(Funcdata *fd);	///< Check if this jump-table requires an additional recovery stage
  void clear(void);				///< Clear instance specific data for \b this jump-table
  void encode(Encoder &encoder) const;		///< Encode \b this jump-table as a \<jumptable> element
  void decode(Decoder &decoder);		///< Decode \b this jump-table from a \<jumptable> element
};

/// \param op2 is the other IndexPair to compare with \b this
/// \return \b true if \b this is ordered before the other IndexPair
inline bool JumpTable::IndexPair::operator<(const IndexPair &op2) const

{
  if (blockPosition != op2.blockPosition) return (blockPosition < op2.blockPosition);
  return (addressIndex < op2.addressIndex);
}

/// \param op1 is the first IndexPair to compare
/// \param op2 is the second IndexPair to compare
/// \return \b true if op1 is ordered before op2
inline bool JumpTable::IndexPair::compareByPosition(const IndexPair &op1,const IndexPair &op2)

{
  return (op1.blockPosition < op2.blockPosition);
}

} // End namespace ghidra
#endif
