/* ###
 * 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 "variable.hh"
#include "op.hh"
#include "expression.hh"
#include "database.hh"

namespace ghidra {

AttributeId ATTRIB_CLASS = AttributeId("class",66);
AttributeId ATTRIB_REPREF = AttributeId("repref",67);
AttributeId ATTRIB_SYMREF = AttributeId("symref",68);

ElementId ELEM_HIGH = ElementId("high",82);

/// Compare by offset within the group, then by size.
/// \param a is the first piece to compare
/// \param b is the other piece to compare
/// \return \b true if \b a should be ordered before the \b b
bool VariableGroup::PieceCompareByOffset::operator()(const VariablePiece *a,const VariablePiece *b) const

{
  if (a->getOffset() != b->getOffset())
    return (a->getOffset() < b->getOffset());
  return (a->getSize() < b->getSize());
}

/// The VariablePiece takes partial ownership of \b this, via refCount.
/// \param piece is the new piece to add
void VariableGroup::addPiece(VariablePiece *piece)

{
  piece->group = this;
  if (!pieceSet.insert(piece).second)
    throw LowlevelError("Duplicate VariablePiece");
  int4 pieceMax = piece->getOffset() + piece->getSize();
  if (pieceMax > size)
    size = pieceMax;
}

/// The adjustment amount must be positive, and this effectively increases the size of the group.
/// \param amt is the given amount to add to offsets
void VariableGroup::adjustOffsets(int4 amt)

{
  set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator iter;

  for(iter=pieceSet.begin();iter!=pieceSet.end();++iter) {
    (*iter)->groupOffset += amt;
  }
  size += amt;
}

void VariableGroup::removePiece(VariablePiece *piece)

{
  pieceSet.erase(piece);
  // We currently don't adjust size here as removePiece is currently only called during clean up
}

/// Every VariablePiece in the given group is moved into \b this and the VariableGroup object is deleted.
/// There must be no matching VariablePieces with the same size and offset between the two groups
/// or a LowlevelError exception is thrown.
/// \param op2 is the given VariableGroup to merge into \b this
void VariableGroup::combineGroups(VariableGroup *op2)

{
  set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator iter = op2->pieceSet.begin();
  set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator enditer = op2->pieceSet.end();

  while(iter != enditer) {
    VariablePiece *piece = *iter;
    ++iter;
    piece->transferGroup(this);
  }
}

/// Construct piece given a HighVariable and its position within the whole.
/// If \b this is the first piece in the group, allocate a new VariableGroup object.
/// \param h is the given HighVariable to treat as a piece
/// \param offset is the byte offset of the piece within the whole
/// \param grp is another HighVariable in the whole, or null if \b this is the first piece
VariablePiece::VariablePiece(HighVariable *h,int4 offset,HighVariable *grp)

{
  high = h;
  groupOffset = offset;
  size = h->getInstance(0)->getSize();
  if (grp != (HighVariable *)0)
    group = grp->piece->getGroup();
  else
    group = new VariableGroup();
  group->addPiece(this);
}

VariablePiece::~VariablePiece(void)

{
  group->removePiece(this);
  if (group->empty())
    delete group;
  else
    markIntersectionDirty();
}

void VariablePiece::markIntersectionDirty(void) const

{
  set<VariablePiece *,VariableGroup::PieceCompareByOffset>::const_iterator iter;

  for(iter=group->pieceSet.begin();iter!=group->pieceSet.end();++iter)
    (*iter)->high->highflags |= (HighVariable::intersectdirty | HighVariable::extendcoverdirty);
}

void VariablePiece::markExtendCoverDirty(void) const

{
  if ((high->highflags & HighVariable::intersectdirty)!=0)
    return;	// intersection list itself is dirty, extended covers will be recomputed anyway
  for(int4 i=0;i<intersection.size();++i) {
    intersection[i]->high->highflags |= HighVariable::extendcoverdirty;
  }
  high->highflags |= HighVariable::extendcoverdirty;
}

/// Compute list of exactly the HighVariable pieces that intersect with \b this.
void VariablePiece::updateIntersections(void) const

{
  if ((high->highflags & HighVariable::intersectdirty)==0) return;
  set<VariablePiece *,VariableGroup::PieceCompareByOffset>::const_iterator iter;

  int4 endOffset = groupOffset + size;
  intersection.clear();
  for(iter=group->pieceSet.begin();iter!=group->pieceSet.end();++iter) {
    VariablePiece *otherPiece = *iter;
    if (otherPiece == this) continue;
    if (endOffset <= otherPiece->groupOffset) continue;
    int4 otherEndOffset = otherPiece->groupOffset + otherPiece->size;
    if (groupOffset >= otherEndOffset) continue;
    intersection.push_back(otherPiece);
  }
  high->highflags &= ~(uint4)HighVariable::intersectdirty;
}

/// Union internal covers of all pieces intersecting with \b this.
void VariablePiece::updateCover(void) const

{
  if ((high->highflags & (HighVariable::coverdirty | HighVariable::extendcoverdirty))==0) return;
  high->updateInternalCover();
  cover = high->internalCover;
  for(int4 i=0;i<intersection.size();++i) {
    const HighVariable *high = intersection[i]->high;
    high->updateInternalCover();
    cover.merge(high->internalCover);
  }
  high->highflags &= ~(uint4)HighVariable::extendcoverdirty;
}

/// If there are no remaining references to the old VariableGroup it is deleted.
/// \param newGroup is the new VariableGroup to transfer \b this to
void VariablePiece::transferGroup(VariableGroup *newGroup)

{
  group->removePiece(this);
  if (group->empty())
    delete group;
  newGroup->addPiece(this);
}

/// Combine the VariableGroup associated \b this and the given other VariablePiece into one group.
/// Offsets are adjusted so that \b this and the other VariablePiece have the same offset.
/// Combining in this way requires pieces of the same size and offset to be merged. This
/// method does not do the merging but passes back a list of HighVariable pairs that need to be merged.
/// The first element in the pair will have its VariablePiece in the new group, and the second element
/// will have its VariablePiece freed in preparation for the merge.
/// \param op2 is the given other VariablePiece
/// \param mergePairs passes back the collection of HighVariable pairs that must be merged
void VariablePiece::mergeGroups(VariablePiece *op2,vector<HighVariable *> &mergePairs)

{
  int4 diff = groupOffset - op2->groupOffset;	// Add to op2, or subtract from this
  if (diff > 0)
    op2->group->adjustOffsets(diff);
  else if (diff < 0)
    group->adjustOffsets(-diff);
  set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator iter = op2->group->pieceSet.begin();
  set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator enditer = op2->group->pieceSet.end();
  while(iter != enditer) {
    VariablePiece *piece = *iter;
    ++iter;
    set<VariablePiece *,VariableGroup::PieceCompareByOffset>::iterator matchiter = group->pieceSet.find(piece);
    if (matchiter != group->pieceSet.end()) {
      mergePairs.push_back((*matchiter)->high);
      mergePairs.push_back(piece->high);
      piece->high->piece = (VariablePiece *)0;	// Detach HighVariable from its original VariablePiece
      delete piece;
    }
    else
      piece->transferGroup(group);
  }
}

/// The new instance starts off with no associate Symbol and all properties marked as \e dirty.
/// \param vn is the single Varnode member
HighVariable::HighVariable(Varnode *vn)

{
  numMergeClasses = 1;
  highflags = flagsdirty | namerepdirty | typedirty | coverdirty;
  flags = 0;
  type = (Datatype *)0;
  piece = (VariablePiece *)0;
  symbol = (Symbol *)0;
  nameRepresentative = (Varnode *)0;
  symboloffset = -1;
  inst.push_back(vn);
  vn->setHigh( this, numMergeClasses-1 );
  if (vn->getSymbolEntry() != (SymbolEntry *)0)
    setSymbol(vn);
}

HighVariable::~HighVariable(void)

{
  if (piece != (VariablePiece *)0)
    delete piece;
}

/// The given Varnode \b must be a member and \b must have a non-null SymbolEntry
void HighVariable::setSymbol(Varnode *vn) const

{
  SymbolEntry *entry = vn->getSymbolEntry();
  if (symbol != (Symbol *)0 && symbol != entry->getSymbol()) {
    if ((highflags & symboldirty)==0) {
      ostringstream s;
      s << "Symbols \"" << symbol->getName() << "\" and \"" << entry->getSymbol()->getName();
      s << "\" assigned to the same variable";
      throw LowlevelError(s.str());
    }
  }
  symbol = entry->getSymbol();
  if (vn->isProtoPartial() && piece != (VariablePiece *)0) {
    symboloffset = piece->getOffset() + piece->getGroup()->getSymbolOffset();
  }
  else if (entry->isDynamic())	// Dynamic symbols (that aren't partials) match whole variable
    symboloffset = -1;
  else if (symbol->getCategory() == Symbol::equate)
    symboloffset = -1;			// For equates, we don't care about size
  else if (symbol->getType()->getSize() == vn->getSize() &&
      entry->getAddr() == vn->getAddr() && !entry->isPiece())
    symboloffset = -1;			// A matching entry
  else {
    symboloffset = vn->getAddr().overlapJoin(0,entry->getAddr(),symbol->getType()->getSize()) + entry->getOffset();
  }

  if (type != (Datatype *)0 && type->getMetatype() == TYPE_PARTIALUNION)
    highflags |= typedirty;
  highflags &= ~((uint4)symboldirty);		// We are no longer dirty
}

/// Link information to \b this from a Symbol that is not attached to a member Varnode.
/// This only works for a HighVariable with a constant member Varnode.  This used when there
/// is a constant address reference to the Symbol and the Varnode holds the reference, not
/// the actual value of the Symbol.
/// \param sym is the given Symbol to attach
/// \param off is the byte offset into the Symbol of the reference
void HighVariable::setSymbolReference(Symbol *sym,int4 off)

{
  symbol = sym;
  symboloffset = off;
  highflags &= ~((uint4)symboldirty);
}

void HighVariable::transferPiece(HighVariable *tv2)

{
  piece = tv2->piece;
  tv2->piece = (VariablePiece *)0;
  piece->setHigh(this);
  highflags |= (tv2->highflags & (intersectdirty | extendcoverdirty));
  tv2->highflags &= ~(uint4)(intersectdirty | extendcoverdirty);
}

/// Except in specific circumstances, convert \b type into its stripped form.
void HighVariable::stripType(void) const

{
  if (!type->hasStripped())
    return;
  type_metatype meta = type->getMetatype();
  if (meta == TYPE_PARTIALUNION || meta == TYPE_PARTIALSTRUCT) {
    if (symbol != (Symbol *)0 && symboloffset != -1) {	// If there is a bigger backing symbol
	type_metatype submeta = symbol->getType()->getMetatype();
	if (submeta == TYPE_STRUCT || submeta == TYPE_UNION)
	  return;			// Don't strip the partial union
    }
  }
  else if (type->isEnumType()) {
    if (inst.size() == 1 && inst[0]->isConstant())	// Only preserve partial enum on a constant
      return;
  }
  type = type->getStripped();
}

/// Only update if the cover is marked as \e dirty.
/// Merge the covers of all Varnode instances.
void HighVariable::updateInternalCover(void) const

{
  if ((highflags & coverdirty) != 0) {
    internalCover.clear();
    if (inst[0]->hasCover()) {
      for(int4 i = 0;i < inst.size();++i)
	internalCover.merge(*inst[i]->getCover());
    }
    highflags &= ~coverdirty;
  }
}

/// This is \b only called by the Merge class which knows when to call it properly.
void HighVariable::updateCover(void) const

{
  if (piece == (VariablePiece *)0)
    updateInternalCover();
  else {
    piece->updateIntersections();
    piece->updateCover();
  }
}

/// Only update if flags are marked as \e dirty.
/// Generally if any member Varnode possesses the property, \b this HighVariable should
/// inherit it.  The Varnode::typelock field is not set here, but in updateType().
void HighVariable::updateFlags(void) const

{
  if ((highflags & flagsdirty)==0) return; // flags are up to date

  vector<Varnode *>::const_iterator iter;
  uint4 fl = 0;

  for(iter=inst.begin();iter!=inst.end();++iter)
    fl |= (*iter)->getFlags();

				// Keep these flags
  flags &= (Varnode::mark | Varnode::typelock);
				// Update all but these
  flags |= fl & ~(Varnode::mark | Varnode::directwrite | Varnode::typelock );
  highflags &= ~flagsdirty; // Clear the dirty flag
}

/// Find the member Varnode with the most \e specialized data-type, handling \e bool specially.
/// Boolean data-types are \e specialized in the data-type lattice, but not all byte values are boolean values.
/// Within the Varnode/PcodeOp tree, the \e bool data-type can only propagate to a Varnode if it is verified to
/// only take the boolean values 0 and 1. Since the data-type representative represents the type of all
/// instances, if any instance is not boolean, then the HighVariable cannot be boolean, even though \e bool
/// is more specialized. This method uses Datatype::typeOrderBool() to implement the special handling.
/// \return the representative member
Varnode *HighVariable::getTypeRepresentative(void) const

{
  vector<Varnode *>::const_iterator iter;
  Varnode *vn,*rep;
  
  iter = inst.begin();
  rep = *iter;
  ++iter;
  for(;iter!=inst.end();++iter) {
    vn = *iter;
    if (rep->isTypeLock() != vn->isTypeLock()) {
      if (vn->isTypeLock())
	rep = vn;
    }
    else if (0>vn->getType()->typeOrderBool(*rep->getType()))
      rep = vn;
  }
  return rep;
}

/// Only update if the data-type is marked as \e dirty.
/// Get the most locked, most specific data-type from member Varnode objects.
void HighVariable::updateType(void) const

{
  Varnode *vn;

  if ((highflags&typedirty)==0) return; // Type is up to date
  highflags &= ~typedirty; // Mark type as clean
  if ((highflags & type_finalized)!=0) return;	// Type has been finalized
  vn = getTypeRepresentative();

  type = vn->getType();
  stripType();
				// Update lock flags
  flags &= ~Varnode::typelock;
  if (vn->isTypeLock())
    flags |= Varnode::typelock;
}

void HighVariable::updateSymbol(void) const

{
  if ((highflags & symboldirty)==0) return; // flags are up to date
  highflags &= ~((uint4)symboldirty);
  vector<Varnode *>::const_iterator iter;
  symbol = (Symbol *)0;

  for(iter=inst.begin();iter!=inst.end();++iter) {
    Varnode *vn = *iter;
    if (vn->getSymbolEntry() != (SymbolEntry *)0) {
      setSymbol(vn);
      return;
    }
  }
}

/// Compare two Varnode objects based just on their storage address
/// \param a is the first Varnode to compare
/// \param b is the second Varnode
/// \return \b true if the first Varnode should be ordered before the second
bool HighVariable::compareJustLoc(const Varnode *a,const Varnode *b)

{
  return (a->getAddr() < b->getAddr());
}

/// Given two Varnode (members), sort them based on naming properties:
///  - A Varnode with an assigned name is preferred
///  - An \e unaffected Varnode is preferred
///  - A global Varnode is preferred
///  - An \e input Varnode is preferred
///  - An \e address \e tied Varnode is preferred
///  - A non-temporary Varnode is preferred
///  - A written Varnode is preferred
///  - An earlier Varnode is preferred
///
/// \return \b true if the second Varnode's name would override the first's
bool HighVariable::compareName(Varnode *vn1,Varnode *vn2)

{
  if (vn1->isNameLock()) return false; // Check for namelocks
  if (vn2->isNameLock()) return true;

  if (vn1->isUnaffected() != vn2->isUnaffected()) // Prefer unaffected
    return vn2->isUnaffected();
  if (vn1->isPersist() != vn2->isPersist()) // Prefer persistent
    return vn2->isPersist();
  if (vn1->isInput() != vn2->isInput())	// Prefer an input
    return vn2->isInput();
  if (vn1->isAddrTied() != vn2->isAddrTied()) // Prefer address tied
    return vn2->isAddrTied();
  if (vn1->isProtoPartial() != vn2->isProtoPartial())	// Prefer pieces
    return vn2->isProtoPartial();

  // Prefer NOT internal
  if ((vn1->getSpace()->getType() != IPTR_INTERNAL)&&
      (vn2->getSpace()->getType() == IPTR_INTERNAL))
    return false;
  if ((vn1->getSpace()->getType() == IPTR_INTERNAL)&&
      (vn2->getSpace()->getType() != IPTR_INTERNAL))
    return true;
  if (vn1->isWritten() != vn2->isWritten()) // Prefer written
    return vn2->isWritten();
  if (!vn1->isWritten())
    return false;
  // Prefer earlier
  if (vn1->getDef()->getTime() != vn2->getDef()->getTime())
    return (vn2->getDef()->getTime() < vn1->getDef()->getTime());
  return false;
}

/// Members are scored based the properties that are most dominating in choosing a name.
/// \return the highest scoring Varnode member
Varnode *HighVariable::getNameRepresentative(void) const

{
  if ((highflags & namerepdirty)==0)
    return nameRepresentative;		// Name representative is up to date
  highflags &= ~namerepdirty;

  vector<Varnode *>::const_iterator iter;
  Varnode *vn;

  iter = inst.begin();
  nameRepresentative = *iter;
  ++iter;
  for(;iter!=inst.end();++iter) {
    vn = *iter;
    if (compareName(nameRepresentative,vn))
      nameRepresentative = vn;
  }
  return nameRepresentative;
}

/// Search for the given Varnode and cut it out of the list, marking all properties as \e dirty.
/// \param vn is the given Varnode member to remove
void HighVariable::remove(Varnode *vn)

{
  vector<Varnode *>::iterator iter;

  iter = lower_bound(inst.begin(),inst.end(),vn,compareJustLoc);
  for(;iter!=inst.end();++iter) {
    if (*iter == vn) {
      inst.erase(iter);
      highflags |= (flagsdirty|namerepdirty|coverdirty|typedirty);
      if (vn->getSymbolEntry() != (SymbolEntry *)0)
	highflags |= symboldirty;
      if (piece != (VariablePiece *)0)
	piece->markExtendCoverDirty();
      return;
    }
  }
}

/// Assuming there is a Symbol attached to \b this, run through the Varnode members
/// until we find one with a SymbolEntry corresponding to the Symbol and return it.
/// \return the SymbolEntry that mapped the Symbol to \b this or null if no Symbol is attached
SymbolEntry *HighVariable::getSymbolEntry(void) const

{
  for(int4 i=0;i<inst.size();++i) {
    SymbolEntry *entry = inst[i]->getSymbolEntry();
    if (entry != (SymbolEntry *)0 && entry->getSymbol() == symbol)
      return entry;
  }
  return (SymbolEntry *)0;
}

/// If there is an associated Symbol, its data-type (or the appropriate piece) is assigned
/// to \b this. The dirtying mechanism is disabled so that data-type cannot change.
/// \param typeFactory is the factory used to construct any required piece
void HighVariable::finalizeDatatype(TypeFactory *typeFactory)

{
  if (symbol == (Symbol *)0) return;
  Datatype *cur = symbol->getType();
  int4 off = symboloffset;
  if (off < 0)
    off = 0;
  int4 sz = inst[0]->getSize();
  Datatype *tp = typeFactory->getExactPiece(cur, off, sz);
  if (tp == (Datatype *)0 || tp->getMetatype() == TYPE_UNKNOWN)
    return;
  type = tp;
  stripType();
  highflags |= type_finalized;
}

/// If one of the HighVariables is already in a group, the other HighVariable is added to this group.
/// \param off is the relative byte offset of \b this with the other HighVariable
/// \param hi2 is the other HighVariable
void HighVariable::groupWith(int4 off,HighVariable *hi2)

{
  if (piece == (VariablePiece *)0 && hi2->piece == (VariablePiece *)0) {
    hi2->piece = new VariablePiece(hi2,0);
    piece = new VariablePiece(this,off,hi2);
    hi2->piece->markIntersectionDirty();
    return;
  }
  if (piece == (VariablePiece *)0) {
    if ((hi2->highflags & intersectdirty) == 0)
      hi2->piece->markIntersectionDirty();
    highflags |= intersectdirty | extendcoverdirty;
    off += hi2->piece->getOffset();
    piece = new VariablePiece(this,off,hi2);
  }
  else if (hi2->piece == (VariablePiece *)0) {
    int4 hi2Off = piece->getOffset() - off;
    if (hi2Off < 0) {
      piece->getGroup()->adjustOffsets(-hi2Off);
      hi2Off = 0;
    }
    if ((highflags & intersectdirty) == 0)
      piece->markIntersectionDirty();
    hi2->highflags |= intersectdirty | extendcoverdirty;
    hi2->piece = new VariablePiece(hi2,hi2Off,this);
  }
  else {
    int4 offDiff = hi2->piece->getOffset() + off - piece->getOffset();
    if (offDiff != 0)
      piece->getGroup()->adjustOffsets(offDiff);
    hi2->piece->getGroup()->combineGroups(piece->getGroup());
    hi2->piece->markIntersectionDirty();
  }
}

/// If \b this is part of a larger group and has had its \b symboloffset set, it can be used
/// to calculate the \b symboloffset of other HighVariables in the same group, by writing it
/// to the common VariableGroup object.
void HighVariable::establishGroupSymbolOffset(void)

{
  VariableGroup *group = piece->getGroup();
  int4 off = symboloffset;
  if (off < 0)
    off = 0;
  off -= piece->getOffset();
  if (off < 0)
    throw LowlevelError("Symbol offset is incompatible with VariableGroup");
  group->setSymbolOffset(off);
}

/// The lists of members are merged and the other HighVariable is deleted.
/// \param tv2 is the other HighVariable to merge into \b this
/// \param isspeculative is \b true to keep the new members in separate \e merge classes
void HighVariable::mergeInternal(HighVariable *tv2,bool isspeculative)

{
  int4 i;

  highflags |= (flagsdirty|namerepdirty|typedirty);
  if (tv2->symbol != (Symbol *)0) {		// Check if we inherit a Symbol
    if ((tv2->highflags & symboldirty)==0) {
      symbol = tv2->symbol;			// Overwrite our Symbol (assume it is the same)
      symboloffset = tv2->symboloffset;
      highflags &= ~((uint4)symboldirty);	// Mark that we are not symbol dirty
    }
  }

  if (isspeculative) {
    for(i=0;i<tv2->inst.size();++i) {
      Varnode *vn = tv2->inst[i];
      vn->setHigh(this,vn->getMergeGroup() + numMergeClasses);
    }
    numMergeClasses += tv2->numMergeClasses;
  }
  else {
    if ((numMergeClasses!=1)||(tv2->numMergeClasses!=1))
      throw LowlevelError("Making a non-speculative merge after speculative merges have occurred");
    for(i=0;i<tv2->inst.size();++i) {
      Varnode *vn = tv2->inst[i];
      vn->setHigh(this,vn->getMergeGroup());
    }
  }
  vector<Varnode *> instcopy(inst);
  inst.resize(inst.size()+tv2->inst.size(),(Varnode *)0);
  std::merge(instcopy.begin(),instcopy.end(),tv2->inst.begin(),tv2->inst.end(),inst.begin(),compareJustLoc);
  tv2->inst.clear();

  if (((highflags&coverdirty)==0)&&((tv2->highflags&coverdirty)==0))
    internalCover.merge(tv2->internalCover);
  else
    highflags |= coverdirty;

  delete tv2;
}

/// The HighVariables are merged internally as with mergeInternal.  If \b this is part of a VariableGroup,
/// extended covers of the group may be affected.  If both HighVariables are part of separate groups,
/// the groups are combined into one, which may induce additional HighVariable pairs within the group to be merged.
/// In all cases, the other HighVariable is deleted.
/// \param tv2 is the other HighVariable to merge into \b this
/// \param testCache if non-null is a cache of intersection tests that must be updated to reflect the merge
/// \param isspeculative is \b true to keep the new members in separate \e merge classes
void HighVariable::merge(HighVariable *tv2,HighIntersectTest *testCache,bool isspeculative)

{
  if (tv2 == this) return;

  if (testCache != (HighIntersectTest *)0)
    testCache->moveIntersectTests(this,tv2);
  if (piece == (VariablePiece *)0 && tv2->piece == (VariablePiece *)0) {
    mergeInternal(tv2,isspeculative);
    return;
  }
  if (tv2->piece == (VariablePiece *)0) {
    // Keep group that this is already in
    piece->markExtendCoverDirty();
    mergeInternal(tv2,isspeculative);
    return;
  }
  if (piece == (VariablePiece *)0) {
    // Move ownership of the VariablePiece object from the HighVariable that will be freed
    transferPiece(tv2);
    piece->markExtendCoverDirty();
    mergeInternal(tv2,isspeculative);
    return;
  }
  // Reaching here both HighVariables are part of a group
  if (isspeculative)
    throw LowlevelError("Trying speculatively merge variables in separate groups");
  vector<HighVariable *> mergePairs;
  piece->mergeGroups(tv2->piece, mergePairs);
  for(int4 i=0;i<mergePairs.size();i+=2) {
    HighVariable *high1 = mergePairs[i];
    HighVariable *high2 = mergePairs[i+1];
    if (testCache != (HighIntersectTest *)0)
      testCache->moveIntersectTests(high1, high2);
    high1->mergeInternal(high2, isspeculative);
  }
  piece->markIntersectionDirty();
}

/// All Varnode objects are assigned a HighVariable, including those that don't get names like
/// indirect variables, constants, and annotations.  Determine if \b this, as inherited from its
/// member Varnodes, can have a name.
/// \return \b true if \b this can have a name
bool HighVariable::hasName(void) const

{
  bool indirectonly = true;
  for(int4 i=0;i<inst.size();++i) {
    Varnode *vn = inst[i];
    if (!vn->hasCover()) {
      if (inst.size() > 1)
	throw LowlevelError("Non-coverable varnode has been merged");
      return false;
    }
    if (vn->isImplied()) {
      if (inst.size() > 1)
	throw LowlevelError("Implied varnode has been merged");
      return false;
    }
    if (!vn->isIndirectOnly())
      indirectonly = false;
  }
  if (isUnaffected()) {
    if (!isInput()) return false;
    if (indirectonly) return false;
    Varnode *vn = getInputVarnode();
    if (!vn->isIllegalInput()) { // A leftover unaff illegal input gets named
      if (vn->isSpacebase())	// A legal input, unaff, gets named
	return false;		// Unless it is the stackpointer
    }
  }
  return true;
}

/// This should only be called if isAddrTied() returns \b true. If there is no address tied
/// member, this will throw an exception.
/// \return the first address tied member
Varnode *HighVariable::getTiedVarnode(void) const

{
  int4 i;

  for(i=0;i<inst.size();++i)
    if (inst[i]->isAddrTied())
      return inst[i];

  throw LowlevelError("Could not find address-tied varnode");
}

/// This should only be called if isInput() returns \b true. If there is no input
/// member, this will throw an exception.
/// \return the input Varnode member
Varnode *HighVariable::getInputVarnode(void) const

{
  for(int4 i=0;i<inst.size();++i)
    if (inst[i]->isInput())
      return inst[i];
  throw LowlevelError("Could not find input varnode");
}

/// This is generally used for debug purposes.
/// \param s is the output stream
void HighVariable::printInfo(ostream &s) const

{
  vector<Varnode *>::const_iterator viter;
  Varnode *vn;

  updateType();
  if (symbol == (Symbol *)0) {
    s << "Variable: UNNAMED" << endl;
  }
  else {
    s << "Variable: " << symbol->getName();
    if (symboloffset!=-1)
      s << "(partial)";
    s << endl;
  }
  s << "Type: ";
  type->printRaw(s);
  s << "\n\n";
				
  for(viter=inst.begin();viter!=inst.end();++viter) {
    vn = *viter;
    s << dec << vn->getMergeGroup() << ": ";
    vn->printInfo(s);
  }
}

/// Find the index, for use with getInstance(), that will retrieve the given Varnode member
/// \param vn is the given Varnode member
/// \return the index of the member or -1 if it is not a member
int4 HighVariable::instanceIndex(const Varnode *vn) const

{
  int4 i;

  for(i=0;i<inst.size();++i)
    if (inst[i] == vn) return i;

  return -1;
}

/// \param encoder is the stream encoder
void HighVariable::encode(Encoder &encoder) const

{
  Varnode *vn = getNameRepresentative(); // Get representative varnode
  encoder.openElement(ELEM_HIGH);
  encoder.writeUnsignedInteger(ATTRIB_REPREF, vn->getCreateIndex());
  if (isSpacebase()||isImplied()) // This is a special variable
    encoder.writeString(ATTRIB_CLASS, "other");
  else if (isPersist()&&isAddrTied()) // Global variable
    encoder.writeString(ATTRIB_CLASS, "global");
  else if (isConstant())
    encoder.writeString(ATTRIB_CLASS, "constant");
  else if (!isPersist() && (symbol != (Symbol *)0)) {
    if (symbol->getCategory() == Symbol::function_parameter)
      encoder.writeString(ATTRIB_CLASS, "param");
    else if (symbol->getScope()->isGlobal())
      encoder.writeString(ATTRIB_CLASS, "global");
    else
      encoder.writeString(ATTRIB_CLASS, "local");
  }
  else {
    encoder.writeString(ATTRIB_CLASS, "other");
  }
  if (isTypeLock())
    encoder.writeBool(ATTRIB_TYPELOCK, true);
  if (symbol != (Symbol *)0) {
    encoder.writeUnsignedInteger(ATTRIB_SYMREF, symbol->getId());
    if (symboloffset >= 0)
      encoder.writeSignedInteger(ATTRIB_OFFSET, symboloffset);
  }
  getType()->encodeRef(encoder);
  for(int4 j=0;j<inst.size();++j) {
    encoder.openElement(ELEM_ADDR);
    encoder.writeUnsignedInteger(ATTRIB_REF, inst[j]->getCreateIndex());
    encoder.closeElement(ELEM_ADDR);
  }
  encoder.closeElement(ELEM_HIGH);
}

/// Given a Varnode at the root of an expression, we collect all the \e explicit HighVariables
/// involved in the expression.  This should only be run after \e explicit and \e implicit
/// properties have been computed on Varnodes.  The expression is traced back from the root
/// until explicit Varnodes are encountered; then their HighVariable is marked and added to the list.
/// The routine returns a value based on PcodeOps encountered in the expression:
///   - 1 for call instructions
///   - 2 for LOAD instructions
///   - 3 for both call and LOAD
///   - 0 for no calls or LOADS
///
/// \param vn is the given root Varnode of the expression
/// \param highList will hold the collected HighVariables
/// \return a value based on call and LOAD instructions in the expression
int4 HighVariable::markExpression(Varnode *vn,vector<HighVariable *> &highList)

{
  HighVariable *high = vn->getHigh();
  high->setMark();
  highList.push_back(high);
  int4 retVal = 0;
  if (!vn->isWritten()) return retVal;

  vector<PcodeOpNode> path;
  PcodeOp *op = vn->getDef();
  if (op->isCall())
    retVal |= 1;
  if (op->code() == CPUI_LOAD)
    retVal |= 2;
  path.push_back(PcodeOpNode(op,0));
  while(!path.empty()) {
    PcodeOpNode &node(path.back());
    if (node.op->numInput() <= node.slot) {
      path.pop_back();
      continue;
    }
    Varnode *curVn = node.op->getIn(node.slot);
    node.slot += 1;
    if (curVn->isAnnotation()) continue;
    if (curVn->isExplicit()) {
      high = curVn->getHigh();
      if (high->isMark()) continue;	// Already in the list
      high->setMark();
      highList.push_back(high);
      continue;				// Truncate at explicit
    }
    if (!curVn->isWritten()) continue;
    op = curVn->getDef();
    if (op->isCall())
      retVal |= 1;
    if (op->code() == CPUI_LOAD)
      retVal |= 2;
    path.push_back(PcodeOpNode(curVn->getDef(),0));
  }
  return retVal;
}

#ifdef MERGEMULTI_DEBUG
/// \brief Check that there are no internal Cover intersections within \b this
///
/// Look for any pair of Varnodes whose covers intersect, but they are not
/// COPY shadows.  Throw an exception in this case.
void HighVariable::verifyCover(void) const

{
  Cover accumCover;

  for(int4 i=0;i<inst.size();++i) {
    Varnode *vn = inst[i];
    if (accumCover.intersect(*vn->getCover()) == 2) {
      for(int4 j=0;j<i;++j) {
	Varnode *otherVn = inst[j];
	if (otherVn->getCover()->intersect(*vn->getCover())==2) {
	  if (!otherVn->copyShadow(vn))
	    throw LowlevelError("HighVariable has internal intersection");
	}
      }
    }
    accumCover.merge(*vn->getCover());
  }
}
#endif

/// \brief Gather Varnode instances of the given HighVariable that intersect a cover on a specific block
///
/// \param a is the given HighVariable
/// \param blk is the specific block number
/// \param cover is the Cover to test for intersection
/// \param res will hold the resulting intersecting Varnodes
void HighIntersectTest::gatherBlockVarnodes(HighVariable *a,int4 blk,const Cover &cover,vector<Varnode *> &res)

{
  for(int4 i=0;i<a->numInstances();++i) {
    Varnode *vn = a->getInstance(i);
    if (1<vn->getCover()->intersectByBlock(blk,cover))
      res.push_back(vn);
  }
}

/// \brief Test instances of a the given HighVariable for intersection on a specific block with a cover
///
/// A list of Varnodes has already been determined to intersect on the block.  For an instance that does as
/// well, a final test of copy shadowing is performed with the Varnode list.  If there is no shadowing,
/// a merging intersection has been found and \b true is returned.
/// \param a is the given HighVariable
/// \param blk is the specific block number
/// \param cover is the Cover to test for intersection
/// \param relOff is the relative byte offset of the HighVariable to the Varnodes
/// \param blist is the list of Varnodes for copy shadow testing
/// \return \b true if there is an intersection preventing merging
bool HighIntersectTest::testBlockIntersection(HighVariable *a,int4 blk,const Cover &cover,int4 relOff,
					      const vector<Varnode *> &blist)
{
  for(int4 i=0;i<a->numInstances();++i) {
    Varnode *vn = a->getInstance(i);
    if (2>vn->getCover()->intersectByBlock(blk,cover)) continue;
    for(int4 j=0;j<blist.size();++j) {
      Varnode *vn2 = blist[j];
      if (1<vn2->getCover()->intersectByBlock(blk,*vn->getCover())) {
	if (vn->getSize() == vn2->getSize()) {
	  if (!vn->copyShadow(vn2))
	    return true;
	}
	else {
	  if (!vn->partialCopyShadow(vn2,relOff))
	    return true;
	}
      }
    }
  }
  return false;
}

/// \brief Test if two HighVariables intersect on a given BlockBasic
///
/// Intersections are checked only on the specified block.
/// \param a is the first HighVariable
/// \param b is the second HighVariable
/// \param blk is the index of the BlockBasic on which to test intersection
/// \return \b true if an intersection occurs in the specified block
bool HighIntersectTest::blockIntersection(HighVariable *a,HighVariable *b,int4 blk)

{
  vector<Varnode *> blist;

  const Cover &aCover(a->getCover());
  const Cover &bCover(b->getCover());
  gatherBlockVarnodes(b,blk,aCover,blist);
  if (testBlockIntersection(a, blk, bCover, 0, blist))
    return true;
  if (a->piece != (VariablePiece *)0) {
    int4 baseOff = a->piece->getOffset();
    for(int4 i=0;i<a->piece->numIntersection();++i) {
      const VariablePiece *interPiece = a->piece->getIntersection(i);
      int4 off = interPiece->getOffset() - baseOff;
      if (testBlockIntersection(interPiece->getHigh(), blk, bCover, off, blist))
	return true;
    }
  }
  if (b->piece != (VariablePiece *)0) {
    int4 bBaseOff = b->piece->getOffset();
    for(int4 i=0;i<b->piece->numIntersection();++i) {
      blist.clear();
      const VariablePiece *bPiece = b->piece->getIntersection(i);
      int4 bOff = bPiece->getOffset() - bBaseOff;
      gatherBlockVarnodes(bPiece->getHigh(),blk,aCover,blist);
      if (testBlockIntersection(a, blk, bCover, -bOff, blist))
	return true;
      if (a->piece != (VariablePiece *)0) {
	int4 baseOff = a->piece->getOffset();
	for(int4 j=0;j<a->piece->numIntersection();++j) {
	  const VariablePiece *interPiece = a->piece->getIntersection(j);
	  int4 off = (interPiece->getOffset() - baseOff) - bOff;
	  if (off > 0 && off >= bPiece->getSize()) continue;		// Do a piece and b piece intersect at all
	  if (off < 0 && -off >= interPiece->getSize()) continue;
	  if (testBlockIntersection(interPiece->getHigh(), blk, bCover, off, blist))
	    return true;
	}
      }
    }
  }
  return false;
}

/// All tests for pairs where either the first or second HighVariable matches the given one
/// are removed.
/// \param high is the given HighVariable to purge
void HighIntersectTest::purgeHigh(HighVariable *high)

{
  map<HighEdge,bool>::iterator iterfirst = highedgemap.lower_bound( HighEdge(high,(HighVariable *)0) );
  map<HighEdge,bool>::iterator iterlast = highedgemap.lower_bound( HighEdge(high,(HighVariable *)~((uintp)0)) );

  if (iterfirst == iterlast) return;
  --iterlast;			// Move back 1 to prevent deleting under the iterator
  map<HighEdge,bool>::iterator iter;
  for(iter=iterfirst;iter!=iterlast;++iter)
    highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
  highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
  ++iterlast;			// Restore original range (with possibly new open endpoint)

  highedgemap.erase(iterfirst,iterlast);
}

/// \brief Test if a given HighVariable might intersect an address tied HighVariable during a call
///
/// If an address tied Varnode has aliases, we need to consider it as \e in \e scope during
/// calls, even if the value is never read after the call.  In particular, another Varnode
/// that \e crosses the call is considered to be intersecting with the address tied Varnode.
/// This method tests whether the address tied HighVariable has aliases, then if so,
/// it tests if the given HighVariable intersects a call site.
/// \param tied is the address tied HighVariable
/// \param untied is the given HighVariable to consider for intersection
/// \return \b true if we consider the HighVariables to be intersecting
bool HighIntersectTest::testUntiedCallIntersection(HighVariable *tied,HighVariable *untied)

{
  // If the address tied part is global, we do not need to test for crossings, as the
  // address forcing mechanism should act as a placeholder across calls
  if (tied->isPersist()) return false;
  Varnode *vn = tied->getTiedVarnode();
  if (vn->hasNoLocalAlias()) return false;	// A local variable is only in scope if it has aliases
  if (!affectingOps.isPopulated())
    affectingOps.populate();
  return untied->getCover().intersect(affectingOps,vn);
}

/// \brief Translate any intersection tests for \e high2 into tests for \e high1
///
/// The two variables will be merged and \e high2, as an object, will be freed.
/// We update the cached intersection tests for \e high2 so that they will now apply to new merged \e high1
/// \param high1 is the variable object being kept
/// \param high2 is the variable object being eliminated
void HighIntersectTest::moveIntersectTests(HighVariable *high1,HighVariable *high2)

{
  vector<HighVariable *> yesinter;		// Highs that high2 intersects
  vector<HighVariable *> nointer;		// Highs that high2 does not intersect
  map<HighEdge,bool>::iterator iterfirst = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)0) );
  map<HighEdge,bool>::iterator iterlast = highedgemap.lower_bound( HighEdge(high2,(HighVariable *)~((uintp)0)) );
  map<HighEdge,bool>::iterator iter;

  for(iter=iterfirst;iter!=iterlast;++iter) {
    HighVariable *b = (*iter).first.b;
    if (b == high1) continue;
    if ((*iter).second)		// Save all high2's intersections
      yesinter.push_back(b);	// as they are still valid for the merge
    else {
      nointer.push_back(b);
      b->setMark();		// Mark that high2 did not intersect
    }
  }
				// Do a purge of all high2's tests
  if (iterfirst != iterlast) {	// Delete all the high2 tests
    --iterlast;			// Move back 1 to prevent deleting under the iterator
    for(iter=iterfirst;iter!=iterlast;++iter)
      highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
    highedgemap.erase( HighEdge( (*iter).first.b, (*iter).first.a) );
    ++iterlast;			// Restore original range (with possibly new open endpoint)

    highedgemap.erase(iterfirst,iterlast);
  }

  iter = highedgemap.lower_bound( HighEdge(high1,(HighVariable *)0) );
  while((iter!=highedgemap.end())&&((*iter).first.a == high1)) {
    if (!(*iter).second) {	// If test is intersection==false
      if (!(*iter).first.b->isMark()) // and there was no test with high2
	highedgemap.erase( iter++ ); // Delete the test
      else
	++iter;
    }
    else			// Keep any intersection==true tests
      ++iter;
  }
  vector<HighVariable *>::iterator titer;
  for(titer=nointer.begin();titer!=nointer.end();++titer)
    (*titer)->clearMark();

	// Reinsert high2's intersection==true tests for high1 now
  for(titer=yesinter.begin();titer!=yesinter.end();++titer) {
    highedgemap[ HighEdge(high1,*titer) ] = true;
    highedgemap[ HighEdge(*titer,high1) ] = true;
  }
}

/// As manipulations are made, Cover information gets out of date. A \e dirty flag is used to
/// indicate a particular HighVariable Cover is out-of-date.  This routine checks the \e dirty
/// flag and updates the Cover information if it is set.
/// \param a is the HighVariable to update
/// \return \b true if the HighVariable was not originally dirty
bool HighIntersectTest::updateHigh(HighVariable *a)

{
  if (!a->isCoverDirty()) return true;

  a->updateCover();
  purgeHigh(a);
  return false;
}

/// \brief Test the intersection of two HighVariables and cache the result
///
/// If the Covers of the two variables intersect, this routine returns \b true. To avoid
/// expensive computation on the Cover objects themselves, the test result associated with
/// the pair of HighVariables is cached.
/// \param a is the first HighVariable
/// \param b is the second HighVariable
/// \return \b true if the variables intersect
bool HighIntersectTest::intersection(HighVariable *a,HighVariable *b)

{
  if (a==b) return false;
  bool ares = updateHigh(a);
  bool bres = updateHigh(b);
  if (ares && bres) {		// If neither high was dirty
    map<HighEdge,bool>::iterator iter = highedgemap.find( HighEdge(a,b) );
    if (iter != highedgemap.end()) // If previous test is present
      return (*iter).second;	// Use it
  }

  bool res = false;
  int4 blk;
  vector<int4> blockisect;
  a->getCover().intersectList(blockisect,b->getCover(),2);
  for(blk=0;blk<blockisect.size();++blk) {
    if (blockIntersection(a,b,blockisect[blk])) {
      res = true;
      break;
    }
  }
  if (!res) {
    bool aTied = a->isAddrTied();
    bool bTied = b->isAddrTied();
    if (aTied != bTied) {			// If one variable is address tied and the other isn't
      if (aTied)
	res = testUntiedCallIntersection(a,b);		// Test if the non-tied variable crosses any calls
      else
	res = testUntiedCallIntersection(b,a);
    }
  }
  highedgemap[ HighEdge(a,b) ] = res; // Cache the result
  highedgemap[ HighEdge(b,a) ] = res;
  return res;
}

} // End namespace ghidra
