/* ###
 * 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.
 */
#include "cover.hh"
#include "block.hh"

namespace ghidra {

const CoverBlock Cover::emptyBlock;

/// PcodeOp objects and a CoverBlock start/stop boundaries have
/// a natural ordering that can be used to tell if a PcodeOp falls
/// between boundary points and if CoverBlock objects intersect.
/// Ordering is determined by comparing the values returned by this method.
/// \param op is the PcodeOp and/or boundary point
/// \return a value for comparison
uintm CoverBlock::getUIndex(const PcodeOp *op)

{
  uintp switchval = (uintp)op;
  switch(switchval) {
  case 0:			// Special marker for very beginning of block
    return (uintm)0;
  case 1:			// Special marker for very end of block
    return ~((uintm)0);
  case 2:			// Special marker for input
    return (uintm)0;
  }
  if (op->isMarker()) {
    if (op->code() == CPUI_MULTIEQUAL) // MULTIEQUALs are considered very beginning
      return (uintm)0;
    else if (op->code() == CPUI_INDIRECT) // INDIRECTs are considered to be at
				// the location of the op they are indirect for
      return PcodeOp::getOpFromConst(op->getIn(1)->getAddr())->getSeqNum().getOrder();
  }
  return op->getSeqNum().getOrder();
}

/// Characterize the intersection of \b this range with another CoverBlock.
/// Return:
///   - 0 if there is no intersection
///   - 1 if only the intersection is at boundary points
///   - 2 if a whole interval intersects
///
/// \param op2 is the other CoverBlock to compare
/// \return the intersection characterization
int4 CoverBlock::intersect(const CoverBlock &op2) const

{
  uintm ustart,ustop;
  uintm u2start,u2stop;

  if (empty()) return 0;
  if (op2.empty()) return 0;

  ustart = getUIndex(start);
  ustop = getUIndex(stop);
  u2start = getUIndex(op2.start);
  u2stop = getUIndex(op2.stop);
  if (ustart <= ustop) {
    if (u2start <= u2stop) { // We are both one piece
      if ((ustop<=u2start)||(u2stop<=ustart)) {
	if ((ustart==u2stop)||(ustop==u2start))
	  return 1;		// Boundary intersection
	else
	  return 0;		// No intersection
      }
    }
    else {			// They are two-piece, we are one-piece
      if ((ustart>=u2stop)&&(ustop<=u2start)) {
	if ((ustart==u2stop)||(ustop==u2start))
	  return 1;
	else
	  return 0;
      }
    }
  }
  else {
    if (u2start <= u2stop) { // They are one piece, we are two-piece
      if ((u2start>=ustop)&&(u2stop<=ustart)) {
	if ((u2start==ustop)||(u2stop==ustart))
	  return 1;
	else
	  return 0;
      }
    }
    // If both are two-pieces, then the intersection must be an interval
  }
  return 2;			// Interval intersection
}

/// If the given PcodeOp or boundary point is contained in \b this range, return true.
/// \param point is the given PcodeOp
/// \return \b true if the point is contained
bool CoverBlock::contain(const PcodeOp *point) const

{
  uintm ustart,ustop,upoint;

  if (empty()) return false;
  upoint = getUIndex(point);
  ustart = getUIndex(start);
  ustop = getUIndex(stop);

  if (ustart<=ustop)
    return ((upoint>=ustart)&&(upoint<=ustop));
  return ((upoint<=ustop)||(upoint>=ustart));
}

/// Return:
///   - 0 if point not on boundary
///   - 1 if on tail
///   - 2 if on the defining point
///
/// \param point is the given PcodeOp point
/// \return the characterization
int4 CoverBlock::boundary(const PcodeOp *point) const

{
  uintm val;

  if (empty()) return 0;
  val = getUIndex(point);
  if (getUIndex(start)==val) {
    if (start!=(const PcodeOp *)0)
      return 2;
  }
  if (getUIndex(stop)==val) return 1;
  return 0;
}

/// Compute the union of \b this with the other given CoverBlock,
/// replacing \b this in place.
/// \param op2 is the other given CoverBlock
void CoverBlock::merge(const CoverBlock &op2)

{
  bool internal1,internal2,internal3,internal4;
  uintm ustart,u2start;

  if (op2.empty()) return;	// Nothing to merge in
  if (empty()) {
    start = op2.start;
    stop = op2.stop;
    return;
  }
  ustart = getUIndex(start);
  u2start = getUIndex(op2.start);
				// Is start contained in op2
  internal4 = ((ustart==(uintm)0)&&(op2.stop==(const PcodeOp *)1));
  internal1 = internal4 || op2.contain(start);
				// Is op2.start contained in this
  internal3 = ((u2start==0)&&(stop==(const PcodeOp *)1));
  internal2 = internal3 || contain(op2.start);

  if (internal1&&internal2)
    if ((ustart!=u2start)|| internal3 || internal4) { // Covered entire block
      setAll();
      return;
    }
  if (internal1)
    start = op2.start;		// Pick non-internal start
  else if ((!internal1)&&(!internal2)) { // Disjoint intervals
    if (ustart < u2start)	// Pick earliest start
      stop = op2.stop;		// then take other stop
    else
      start = op2.start;
    return;
  }
  if (internal3 || op2.contain(stop)) // Pick non-internal stop
    stop = op2.stop;
}

/// Print a description of the covered range of ops in this block
/// \param s is the output stream
void CoverBlock::print(ostream &s) const

{
  uintm ustart,ustop;

  if (empty()) {
    s << "empty";
    return;
  }

  ustart = getUIndex(start);
  ustop = getUIndex(stop);
  if (ustart==(uintm)0)
    s << "begin";
  else if (ustart==~((uintm)0))
    s << "end";
  else
    s << start->getSeqNum();

  s << '-';

  if (ustop==(uintm)0)
    s << "begin";
  else if (ustop==~((uintm)0))
    s << "end";
  else
    s << stop->getSeqNum();
}

/// Compare \b this with another Cover by comparing just
/// the indices of the first blocks respectively that are partly covered.
/// Return -1, 0, or 1 if \b this Cover's first block has a
/// smaller, equal, or bigger index than the other Cover's first block.
/// \param op2 is the other Cover
/// \return the comparison value
int4 Cover::compareTo(const Cover &op2) const

{
  int4 a,b;

  map<int4,CoverBlock>::const_iterator iter;
  iter = cover.begin();
  if (iter==cover.end())
    a = 1000000;
  else
    a = (*iter).first;
  iter = op2.cover.begin();
  if (iter==op2.cover.end())
    b = 1000000;
  else
    b = (*iter).first;

  if ( a < b ) {
	return -1;
  }
  else if ( a == b ) {
	return 0;
  }
  return 1;
}

/// Return a representative CoverBlock describing how much of the given block
/// is covered by \b this
/// \param i is the index of the given block
/// \return a reference to the corresponding CoverBlock
const CoverBlock &Cover::getCoverBlock(int4 i) const

{
  map<int4,CoverBlock>::const_iterator iter = cover.find(i);
  if (iter == cover.end())
    return emptyBlock;
  return (*iter).second;
}

/// Return
///   - 0 if there is no intersection
///   - 1 if the only intersection is on a boundary point
///   - 2 if the intersection contains a range of p-code ops
///
/// \param op2 is the other Cover
/// \return the intersection characterization
int4 Cover::intersect(const Cover &op2) const

{
  map<int4,CoverBlock>::const_iterator iter,iter2;
  int4 res,newres;

  res = 0;
  iter = cover.begin();
  iter2 = op2.cover.begin();

  for(;;) {
    if (iter == cover.end()) return res;
    if (iter2 == op2.cover.end()) return res;

    if ((*iter).first < (*iter2).first)
      ++iter;
    else if ((*iter).first > (*iter2).first)
      ++iter2;
    else {
      newres = (*iter).second.intersect((*iter2).second);
      if (newres == 2) return 2;
      if (newres == 1)
	res = 1;		// At least a point intersection
      ++iter;
      ++iter2;
    }
  }
  return res;
}

/// \brief Generate a list of blocks that intersect
///
/// For each block for which \b this and another Cover intersect,
/// and the block's index to a result list if the type of intersection
/// exceeds a characterization level.
/// \param listout will hold the list of intersecting block indices
/// \param op2 is the other Cover
/// \param level is the characterization threshold which must be exceeded
void Cover::intersectList(vector<int4> &listout,const Cover &op2,int4 level) const

{
  map<int4,CoverBlock>::const_iterator iter,iter2;
  int4 val;

  listout.clear();

  iter = cover.begin();
  iter2 = op2.cover.begin();

  for(;;) {
    if (iter == cover.end()) return;
    if (iter2 == op2.cover.end()) return;

    if ((*iter).first < (*iter2).first)
      ++iter;
    else if ((*iter).first > (*iter2).first)
      ++iter2;
    else {
      val = (*iter).second.intersect((*iter2).second);
      if (val >= level)
	listout.push_back((*iter).first);
      ++iter;
      ++iter2;
    }
  }
}

/// If any PcodeOp in the set falls inside \b this Cover, a secondary test that the PcodeOp
/// affects the representative Varnode is performed. If the test returns \b true, this is considered
/// a full intersection and this method returns \b true.  Otherwise it returns \b false.
/// \param opSet is the given set of PcodeOps
/// \param rep is the representative Varnode to use for secondary testing
/// \return \b true is there is an intersection with \b this
bool Cover::intersect(const PcodeOpSet &opSet,Varnode *rep) const

{
  if (opSet.opList.empty()) return false;
  int4 setBlock = 0;
  int4 opIndex = opSet.blockStart[setBlock];
  int4 setIndex = opSet.opList[opIndex]->getParent()->getIndex();
  map<int4,CoverBlock>::const_iterator coverIter = cover.lower_bound(opSet.opList[0]->getParent()->getIndex());
  while(coverIter != cover.end()) {
    int4 coverIndex = (*coverIter).first;
    if (coverIndex < setIndex) {
      ++coverIter;
    }
    else if (coverIndex > setIndex) {
      setBlock += 1;
      if (setBlock >= opSet.blockStart.size()) break;
      opIndex = opSet.blockStart[setBlock];
      setIndex = opSet.opList[opIndex]->getParent()->getIndex();
    }
    else {
      const CoverBlock &coverBlock( (*coverIter).second );
      ++coverIter;
      int4 opMax = opSet.opList.size();
      setBlock += 1;
      if (setBlock < opSet.blockStart.size())
	opMax = opSet.blockStart[setBlock];
      do {
	PcodeOp *op = opSet.opList[opIndex];
	if (coverBlock.contain(op)) {		// Does range contain the call?
	  if (coverBlock.boundary(op) == 0) {	// Is the call on the boundary
	    if (opSet.affectsTest(op, rep))	// Do secondary testing
	      return true;
	  }
	}
	opIndex += 1;
      } while(opIndex < opMax);
      if (setBlock >= opSet.blockStart.size()) break;
    }
  }
  return false;
}

/// Looking only at the given block, Return
///   - 0 if there is no intersection
///   - 1 if the only intersection is on a boundary point
///   - 2 if the intersection contains a range of p-code ops
///
/// \param blk is the index of the given block
/// \param op2 is the other Cover
/// \return the characterization
int4 Cover::intersectByBlock(int4 blk,const Cover &op2) const

{
  map<int4,CoverBlock>::const_iterator iter;

  iter = cover.find(blk);
  if (iter == cover.end()) return 0;
  
  map<int4,CoverBlock>::const_iterator iter2;

  iter2 = op2.cover.find(blk);
  if (iter2 == op2.cover.end()) return 0;

  return (*iter).second.intersect((*iter2).second);
}

/// \brief Does \b this contain the given PcodeOp
///
/// \param op is the given PcodeOp
/// \param max is 1 to test for any containment, 2 to force interior containment
/// \return true if there is containment
bool Cover::contain(const PcodeOp *op,int4 max) const

{
  map<int4,CoverBlock>::const_iterator iter;

  iter = cover.find(op->getParent()->getIndex());
  if (iter == cover.end()) return false;
  if ((*iter).second.contain(op)) {
    if (max==1) return true;
    if (0==(*iter).second.boundary(op)) return true;
  }
  return false;
}

/// \brief Check the definition of a Varnode for containment
///
/// If the given Varnode has a defining PcodeOp this is
/// checked for containment.  If the Varnode is an input,
/// check if \b this covers the start of the function.
///
/// Return:
///   - 0 if cover does not contain varnode definition
///   - 1 if there if it is contained in interior
///   - 2 if the defining points intersect
///   - 3 if Cover's tail is the varnode definition
///
/// \param vn is the given Varnode
/// \return the containment characterization
int4 Cover::containVarnodeDef(const Varnode *vn) const

{
  const PcodeOp *op = vn->getDef();
  int4 blk;

  if (op == (const PcodeOp *)0) {
    op = (const PcodeOp *)2;
    blk = 0;
  }
  else
    blk = op->getParent()->getIndex();
  map<int4,CoverBlock>::const_iterator iter = cover.find(blk);
  if (iter == cover.end()) return 0;
  if ((*iter).second.contain(op)) {
    int4 boundtype = (*iter).second.boundary(op);
    if (boundtype == 0) return 1;
    if (boundtype == 2) return 2;
    return 3;
  }
  return 0;
}

/// \param op2 is the other Cover
void Cover::merge(const Cover &op2)

{
  map<int4,CoverBlock>::const_iterator iter;

  for(iter=op2.cover.begin();iter!=op2.cover.end();++iter)
    cover[(*iter).first].merge((*iter).second);
}

/// The cover is set to all p-code ops between the point where
/// the Varnode is defined and all the points where it is read
/// \param vn is the single Varnode
void Cover::rebuild(const Varnode *vn)

{
  vector<const Varnode *> path(1,vn);
  int4 pos = 0;

  addDefPoint(vn);
  do {
    const Varnode *curVn = path[pos];
    pos += 1;
    list<PcodeOp *>::const_iterator iter;
    for(iter=curVn->beginDescend();iter!=curVn->endDescend();++iter) {
      const PcodeOp *op = *iter;
      addRefPoint(op,vn);
      const Varnode *outVn = op->getOut();
      if (outVn != (Varnode *)0 && outVn->isImplied())
	path.push_back(outVn);
    }
  } while(pos < path.size());
}

/// Any previous cover is removed. Calling this with an
/// input Varnode still produces a valid Cover.
/// \param vn is the Varnode
void Cover::addDefPoint(const Varnode *vn)

{
  const PcodeOp *def;

  cover.clear();

  def = vn->getDef();
  if (def != (const PcodeOp *)0) {
    CoverBlock &block( cover[def->getParent()->getIndex() ] );
    block.setBegin(def);	// Set the point topology
    block.setEnd(def);
  }
  else if (vn->isInput()) {
    CoverBlock &block( cover[0] );
    block.setBegin( (const PcodeOp *)2 ); // Special mark for input
    block.setEnd( (const PcodeOp *)2 );
  }
}

/// Add to \b this Cover recursively, starting at bottom of the given block
/// and filling in backward until we run into existing cover.
/// \param bl is the starting block to add
void Cover::addRefRecurse(const FlowBlock *bl)

{
  int4 j;
  uintm ustart,ustop;

  CoverBlock &block(cover[bl->getIndex()]);
  if (block.empty()) {
    block.setAll();		// No cover encountered, fill in entire block
    //    if (bl->InSize()==0)
    //      throw LowlevelError("Ref point is not in flow of defpoint");
    for(j=0;j<bl->sizeIn();++j)	// Recurse to all blocks that fall into bl
      addRefRecurse(bl->getIn(j));
  }
  else {
    const PcodeOp *op = block.getStop();
    ustart = CoverBlock::getUIndex(block.getStart());
    ustop = CoverBlock::getUIndex(op);
    if ((ustop != ~((uintm)0))&&( ustop >= ustart))
      block.setEnd((const PcodeOp *)1); // Fill in to the bottom


    if ((ustop==(uintm)0)&&(block.getStart() == (const PcodeOp *)0)) {
      if ((op != (const PcodeOp *)0)&&(op->code()==CPUI_MULTIEQUAL)) {
				// This block contains only an infinitesimal tip
				// of cover through one branch of a MULTIEQUAL
				// we still need to traverse through branches
	for(j=0;j<bl->sizeIn();++j)
	  addRefRecurse(bl->getIn(j));
      }
    }


  }
}

/// Given a Varnode being read and the PcodeOp which reads it,
/// add the point of the read to \b this and recursively fill in backwards until
/// we run into existing cover.
/// \param ref is the reading PcodeOp
/// \param vn is the Varnode being read
void Cover::addRefPoint(const PcodeOp *ref,const Varnode *vn)

{
  int4 j;
  const FlowBlock *bl;
  uintm ustop;

  bl = ref->getParent();
  CoverBlock &block(cover[bl->getIndex()]);
  if (block.empty()) {
    block.setEnd(ref);
  }
  else {
    if (block.contain(ref)) {
       if (ref->code() != CPUI_MULTIEQUAL) return;
				// Even if MULTIEQUAL ref is contained
				// we may be adding new cover because we are
				// looking at a different branch. So don't return
    }
    else {
      const PcodeOp *op = block.getStop();
      const PcodeOp *startop = block.getStart();
      block.setEnd(ref);		// Otherwise update endpoint
      ustop = CoverBlock::getUIndex(block.getStop());
      if (ustop >= CoverBlock::getUIndex(startop)) {
	if ((op!=(const PcodeOp *)0)&&(op!=(const PcodeOp *)2)&&
	    (op->code()==CPUI_MULTIEQUAL)&&(startop==(const PcodeOp *)0)) {
				// This block contains only an infinitesimal tip
				// of cover through one branch of a MULTIEQUAL
				// we still need to traverse through branches
	  for(j=0;j<bl->sizeIn();++j)
	    addRefRecurse(bl->getIn(j));
	}
	return;
      }
    }
  }
  //  if (bl->InSize()==0)
  //    throw LowlevelError("Ref point is not in flow of defpoint");
  if (ref->code() == CPUI_MULTIEQUAL) {
    for(j=0;j<ref->numInput();++j)
      if (ref->getIn(j)==vn)
	addRefRecurse(bl->getIn(j));
  }
  else
    for(j=0;j<bl->sizeIn();++j)
      addRefRecurse(bl->getIn(j));
}

/// \param s is the output stream
void Cover::print(ostream &s) const

{
  map<int4,CoverBlock>::const_iterator iter;

  for(iter=cover.begin();iter!=cover.end();++iter) {
    s << dec << (*iter).first << ": ";
    (*iter).second.print(s);
    s << endl;
  }
}

void PcodeOpSet::finalize(void)

{
  sort(opList.begin(),opList.end(),compareByBlock);
  int4 blockNum = -1;
  for(int4 i=0;i<opList.size();++i) {
    int4 newBlockNum = opList[i]->getParent()->getIndex();
    if (newBlockNum > blockNum) {
      blockStart.push_back(i);
      blockNum = newBlockNum;
    }
  }
  is_pop = true;
}

/// Compare first by index of the containing basic blocks, then by SeqNum ordering (within the block)
/// \param a is the first PcodeOp to compare
/// \param b is the second PcodeOp to compare
/// \return \b true if the first PcodeOp should be ordered before the second
bool PcodeOpSet::compareByBlock(const PcodeOp *a,const PcodeOp *b)

{
  if (a->getParent() != b->getParent())
    return (a->getParent()->getIndex() < b->getParent()->getIndex());
  return a->getSeqNum().getOrder() < b->getSeqNum().getOrder();
}

} // End namespace ghidra
