/* ###
 * 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 "double.hh"

namespace ghidra {

/// Internally, the \b lo and \b hi Varnodes are set to null, and the \b val field
/// holds the constant value.
/// \param sz is the size in bytes of the constant
/// \param v is the constant value
SplitVarnode::SplitVarnode(int4 sz,uintb v)

{
  val = v;
  wholesize = sz;
  lo = (Varnode *)0;
  hi = (Varnode *)0;
  whole = (Varnode *)0;
  defpoint = (PcodeOp *)0;
  defblock = (BlockBasic *)0;
}

/// \param sz is the size of the constant in bytes
/// \param v is the constant value
void SplitVarnode::initPartial(int4 sz,uintb v)

{
  val = v;
  wholesize = sz;
  lo = (Varnode *)0;
  hi = (Varnode *)0;
  whole = (Varnode *)0;
  defpoint = (PcodeOp *)0;
  defblock = (BlockBasic *)0;
}

/// The Varnode pieces can be constant, in which case a constant SplitVarnode is initialized and
/// a constant value is built from the pieces.  The given most significant piece can be null, indicating
/// that the most significant piece of the whole is an implied zero.
/// \param sz is the size of the logical whole in bytes
/// \param l is the given (least significant) Varnode piece
/// \param h is the given (most significant) Varnode piece
void SplitVarnode::initPartial(int4 sz,Varnode *l,Varnode *h)

{
  if (h == (Varnode *)0) {	// hi is an implied zero
    hi = (Varnode *)0;
    if (l->isConstant()) {
      val = l->getOffset();	// Assume l is a constant
      lo = (Varnode *)0;
    }
    else
      lo = l;
  }
  else {
    if (l->isConstant() && h->isConstant()) {
      val = h->getOffset();
      val <<= (l->getSize()*8);
      val |= l->getOffset();
      lo = (Varnode *)0;
      hi = (Varnode *)0;
    }
    else {
      lo = l;
      hi = h;
    }
  }
  wholesize = sz;
  whole = (Varnode *)0;
  defpoint = (PcodeOp *)0;
  defblock = (BlockBasic *)0;
}

/// The \b lo, \b hi, and \b whole fields are filled in.  The definition point remains uninitialized.
/// \param w is the given whole Varnode
/// \param l is the given (least significant) Varnode piece
/// \param h is the given (most significant) Varnode piece
void SplitVarnode::initAll(Varnode *w,Varnode *l,Varnode *h)

{
  wholesize = w->getSize();
  lo = l;
  hi = h;
  whole = w;
  defpoint = (PcodeOp *)0;
  defblock = (BlockBasic *)0;
}

/// Verify that the given most significant piece is formed via CPUI_SUBPIECE and search
/// for the least significant piece being formed as a CPUI_SUBPIECE of the same whole.
/// \param h is the given (most significant) Varnode piece
/// \return \b true if the matching \b whole and least significant piece is found
bool SplitVarnode::inHandHi(Varnode *h)

{
  if (!h->isPrecisHi()) return false; // Check for mark, in order to have quick -false- in most cases
  // Search for the companion
  if (h->isWritten()) {
    PcodeOp *op = h->getDef();
    // We could check for double loads here
    if (op->code() == CPUI_SUBPIECE) {
      Varnode *w = op->getIn(0);
      if (op->getIn(1)->getOffset() != (uintb)(w->getSize()-h->getSize())) return false;
      list<PcodeOp *>::const_iterator iter,enditer;
      iter = w->beginDescend();
      enditer = w->endDescend();
      while(iter != enditer) {
	PcodeOp *tmpop = *iter;
	++iter;
	if (tmpop->code() != CPUI_SUBPIECE) continue;
	Varnode *tmplo = tmpop->getOut();
	if (!tmplo->isPrecisLo()) continue;
	if (tmplo->getSize() + h->getSize() != w->getSize()) continue;
	if (tmpop->getIn(1)->getOffset() != 0) continue;
	// There could conceivably be more than one, but this shouldn't happen with CSE
	initAll(w,tmplo,h);
	return true;
      }
    }
  }
  return false;
}

/// Verify that the given least significant piece is formed via CPUI_SUBPIECE and search
/// for the most significant piece being formed as a CPUI_SUBPIECE of the same whole.
/// \param l is the given (least significant) Varnode piece
/// \return \b true if the matching \b whole and most significant piece is found
bool SplitVarnode::inHandLo(Varnode *l)

{
  if (!l->isPrecisLo()) return false; // Check for mark, in order to have quick -false- in most cases
  // Search for the companion
  if (l->isWritten()) {
    PcodeOp *op = l->getDef();
    // We could check for double loads here
    if (op->code() == CPUI_SUBPIECE) {
      Varnode *w = op->getIn(0);
      if (op->getIn(1)->getOffset() != 0) return false;
      list<PcodeOp *>::const_iterator iter,enditer;
      iter = w->beginDescend();
      enditer = w->endDescend();
      while(iter != enditer) {
	PcodeOp *tmpop = *iter;
	++iter;
	if (tmpop->code() != CPUI_SUBPIECE) continue;
	Varnode *tmphi = tmpop->getOut();
	if (!tmphi->isPrecisHi()) continue;
	if (tmphi->getSize() + l->getSize() != w->getSize()) continue;
	if (tmpop->getIn(1)->getOffset() != (uintb)l->getSize()) continue;
	// There could conceivably be more than one, but this shouldn't happen with CSE
	initAll(w,l,tmphi);
	return true;
      }
    }
  }
  return false;
}

/// The given least significant Varnode must already be marked as a piece.
/// Initialize the SplitVarnode with the given piece and the \b whole that it came from.
/// If a matching most significant piece can be found, as another CPUI_SUBPIECE off of the same
/// \b whole, initialize that as well.  Otherwise leave the most significant piece as null.
/// \param l is the given (least significant) Varnode piece
/// \return \b true if the SplitVarnode is successfully initialized
bool SplitVarnode::inHandLoNoHi(Varnode *l)

{
  if (!l->isPrecisLo()) return false;
  if (!l->isWritten()) return false;
  PcodeOp *op = l->getDef();
  if (op->code() != CPUI_SUBPIECE) return false;
  if (op->getIn(1)->getOffset() != 0) return false;
  Varnode *w = op->getIn(0);

  list<PcodeOp *>::const_iterator iter,enditer;
  iter = w->beginDescend();
  enditer = w->endDescend();
  while(iter != enditer) {
    PcodeOp *tmpop = *iter;
    ++iter;
    if (tmpop->code() != CPUI_SUBPIECE) continue;
    Varnode *tmphi = tmpop->getOut();
    if (!tmphi->isPrecisHi()) continue;
    if (tmphi->getSize() + l->getSize() != w->getSize()) continue;
    if (tmpop->getIn(1)->getOffset() != (uintb)l->getSize()) continue;
    // There could conceivably be more than one, but this shouldn't happen with CSE
    initAll(w,l,tmphi);
    return true;
  }
  initAll(w,l,(Varnode *)0);
  return true;
}

/// Initialize the SplitVarnode given the most significant piece, if it is concatenated together
/// immediately with is least significant piece.  The CPUI_PIECE and the matching least significant
/// piece must be unique.  If these are found, \b hi, \b lo, and \b whole are all filled in.
/// \param h is the given (most significant) piece
/// \return \b true if initialization was successful and the least significant piece was found
bool SplitVarnode::inHandHiOut(Varnode *h)

{
  list<PcodeOp *>::const_iterator iter,enditer;
  iter = h->beginDescend();
  enditer = h->endDescend();
  Varnode *loTmp = (Varnode *)0;
  Varnode *outvn = (Varnode *)0;
  while(iter != enditer) {
    PcodeOp *pieceop = *iter;
    ++iter;
    if (pieceop->code() != CPUI_PIECE) continue;
    if (pieceop->getIn(0) != h) continue;
    Varnode *l = pieceop->getIn(1);
    if (!l->isPrecisLo()) continue;
    if (loTmp != (Varnode *)0) return false; // Whole is not unique
    loTmp = l;
    outvn = pieceop->getOut();
  }
  if (loTmp != (Varnode *)0) {
    initAll(outvn,loTmp,h);
    return true;
  }
  return false;
}

/// Initialize the SplitVarnode given the least significant piece, if it is concatenated together
/// immediately with is nost significant piece.  The CPUI_PIECE and the matching most significant
/// piece must be unique.  If these are found, \b hi, \b lo, and \b whole are all filled in.
/// \param l is the given (least significant) piece
/// \return \b true if initialization was successful and the most significant piece was found
bool SplitVarnode::inHandLoOut(Varnode *l)

{
  list<PcodeOp *>::const_iterator iter,enditer;
  iter = l->beginDescend();
  enditer = l->endDescend();
  Varnode *hiTmp = (Varnode *)0;
  Varnode *outvn = (Varnode *)0;
  while(iter != enditer) {
    PcodeOp *pieceop = *iter;
    ++iter;
    if (pieceop->code() != CPUI_PIECE) continue;
    if (pieceop->getIn(1) != l) continue;
    Varnode *h = pieceop->getIn(0);
    if (!h->isPrecisHi()) continue;
    if (hiTmp != (Varnode *)0) return false; // Whole is not unique
    hiTmp = h;
    outvn = pieceop->getOut();
  }
  if (hiTmp != (Varnode *)0) {
    initAll(outvn,l,hiTmp);
    return true;
  }
  return false;
}

/// Look for CPUI_SUBPIECE operations off of a common Varnode.
/// The \b whole field is set to this Varnode if found; the definition point and block are
/// filled in and \b true is returned.  Otherwise \b false is returned.
/// \return \b true if the \b whole Varnode is found
bool SplitVarnode::findWholeSplitToPieces(void)

{
  if (whole == (Varnode*)0) {
    if (hi == (Varnode*)0) return false;
    if (lo == (Varnode*)0) return false;
    if (!hi->isWritten()) return false;
    PcodeOp *subhi = hi->getDef();
    if (subhi->code() == CPUI_COPY) { // Go thru one level of copy, if the piece is addrtied
      Varnode *otherhi = subhi->getIn(0);
      if (!otherhi->isWritten()) return false;
      subhi = otherhi->getDef();
    }
    if (subhi->code() != CPUI_SUBPIECE) return false;
    if (subhi->getIn(1)->getOffset() != wholesize - hi->getSize()) return false;
    Varnode *putativeWhole = subhi->getIn(0);
    if (putativeWhole->getSize() != wholesize) return false;
    if (!lo->isWritten()) return false;
    PcodeOp *sublo = lo->getDef();
    if (sublo->code() == CPUI_COPY) { // Go thru one level of copy, if the piece is addrtied
      Varnode *otherlo = sublo->getIn(0);
      if (!otherlo->isWritten()) return false;
      sublo = otherlo->getDef();
    }
    if (sublo->code() != CPUI_SUBPIECE) return false;
    if (putativeWhole != sublo->getIn(0))
      return false;		// Doesn't match between pieces
    if (sublo->getIn(1)->getOffset() != 0)
      return false;
    whole = putativeWhole;
  }

  if (whole->isWritten()) {
    defpoint = whole->getDef();
    defblock = defpoint->getParent();
  }
  else if (whole->isInput()) {
    defpoint = (PcodeOp *)0;
    defblock = (BlockBasic *)0;
  }
  return true;
}

/// Set the basic block, \b defblock, and PcodeOp, \b defpoint, where they are defined.
/// Its possible that \b lo and \b hi are \e input Varnodes with no natural defining PcodeOp,
/// in which case \b defpoint is set to null and \b defblock is set to the function entry block.
/// The method returns \b true, if the definition point is found, which amounts to returning
/// \b false if the SplitVarnode is only half constant or half input.
/// \return \b true if the definition point is located
bool SplitVarnode::findDefinitionPoint(void)

{
  PcodeOp *lastop;
  if (hi != (Varnode *)0 && hi->isConstant()) return false; // If one but not both is constant
  if (lo->isConstant()) return false;
  if (hi == (Varnode *)0) {	// Implied zero extension
    if (lo->isInput()) {
      defblock = (BlockBasic *)0;
      defpoint = (PcodeOp *)0;
    }
    else if (lo->isWritten()) {
      defpoint = lo->getDef();
      defblock = defpoint->getParent();
    }
    else
      return false;
  }
  else if (hi->isWritten()) {
    if (!lo->isWritten()) return false;		// Do not allow mixed input/non-input pairs
    lastop = hi->getDef();
    defblock = lastop->getParent();
    PcodeOp *lastop2 = lo->getDef();
    BlockBasic *otherblock = lastop2->getParent();
    if (defblock != otherblock) {
      defpoint = lastop;
      FlowBlock *curbl = defblock;
      while(curbl != (FlowBlock *)0) { // Make sure defblock dominated by otherblock
	curbl = curbl->getImmedDom();
	if (curbl == otherblock) return true;
      }
      defblock = otherblock;		// Try lo as final defining location
      otherblock = lastop->getParent();
      defpoint = lastop2;
      curbl = defblock;
      while(curbl != (FlowBlock *)0) {
	curbl = curbl->getImmedDom();
	if (curbl == otherblock) return true;
      }
      defblock = (BlockBasic *)0;
      return false;		// Not defined in same basic block
    }
    if (lastop2->getSeqNum().getOrder() > lastop->getSeqNum().getOrder())
      lastop = lastop2;
    defpoint = lastop;
  }
  else if (hi->isInput()) {
    if (!lo->isInput())
      return false;		// Do not allow mixed input/non-input pairs
    defblock = (BlockBasic *)0;
    defpoint = (PcodeOp *)0;
  }
  return true;
}

/// If both \b lo and \b hi pieces are written, the earlier of the two defining PcodeOps
/// is returned.  Otherwise null is returned.
/// \return the earlier of the two defining PcodeOps or null
PcodeOp *SplitVarnode::findEarliestSplitPoint(void)

{
  if (!hi->isWritten()) return (PcodeOp *)0;
  if (!lo->isWritten()) return (PcodeOp *)0;
  PcodeOp *hiop = hi->getDef();
  PcodeOp *loop = lo->getDef();
  if (loop->getParent() != hiop->getParent())
    return (PcodeOp *)0;
  return (loop->getSeqNum().getOrder() < hiop->getSeqNum().getOrder()) ? loop : hiop;
}
 
/// We scan for concatenations formed out of \b hi and \b lo, in the correct significance order.
/// We assume \b hi and \b lo are defined in the same basic block (or  are both inputs) and that
/// the concatenation is also in this block. If such a concatenation is found, \b whole is set to the
/// concatenated Varnode, the defining block and PcodeOp is filled in, and \b true is returned.
/// \return \b true if a \b whole concatenated from \b hi and \b lo is found
bool SplitVarnode::findWholeBuiltFromPieces(void)

{
  if (hi==(Varnode *)0) return false;
  if (lo==(Varnode *)0) return false;
  list<PcodeOp *>::const_iterator iter,enditer;
  iter = lo->beginDescend();
  enditer = lo->endDescend();
  PcodeOp *res = (PcodeOp *)0;
  BlockBasic *bb;
  if (lo->isWritten())
    bb = lo->getDef()->getParent();
  else if (lo->isInput())
    bb = (BlockBasic *)0;
  else
    throw LowlevelError("Trying to find whole on free varnode");
  while(iter != enditer) {
    PcodeOp *op = *iter;
    ++iter;
    if (op->code() != CPUI_PIECE) continue;
    if (op->getIn(0) != hi) continue;
    if (bb != (BlockBasic *)0) {
      if (op->getParent() != bb) continue; // Not defined in earliest block
    }
    else if (!op->getParent()->isEntryPoint())
      continue;
    if (res == (PcodeOp *)0)
      res = op;
    else {
      if (op->getSeqNum().getOrder() < res->getSeqNum().getOrder()) // Find "earliest" whole
	res = op;
    }
  }

  if (res == (PcodeOp *)0)
    whole = (Varnode *)0;
  else {
    defpoint = res;
    defblock = defpoint->getParent();
    whole = res->getOut();
  }
  return (whole!=(Varnode *)0);
}

/// The whole Varnode must be defined or definable \e before the given PcodeOp.
/// This is checked by comparing the given PcodeOp to the defining PcodeOp and block for \b this,
/// which are filled in if they weren't before.
/// \param existop is the given PcodeOp
/// \return \b true if a whole Varnode exists or can be defined before the given PcodeOp
bool SplitVarnode::isWholeFeasible(PcodeOp *existop)

{
  if (isConstant()) return true;
  if ((lo!=(Varnode *)0)&&(hi!=(Varnode *)0))
    if (lo->isConstant() != hi->isConstant()) return false; // Mixed constant/non-constant
  if (!findWholeSplitToPieces()) {
    if (!findWholeBuiltFromPieces()) {
      if (!findDefinitionPoint())
	return false;
    }
  }
  if (defblock == (BlockBasic *)0) return true;
  FlowBlock *curbl = existop->getParent();
  if (curbl == defblock)	// If defined in same block as -existop- check PcodeOp ordering
    return (defpoint->getSeqNum().getOrder() <= existop->getSeqNum().getOrder());
  while(curbl != (FlowBlock *)0) { // Make sure defbock dominates block containing -existop-
    curbl = curbl->getImmedDom();
    if (curbl == defblock) return true;
  }
  return false;
}

/// This is similar to isWholeFeasible(), but the \b whole must be defined before the end of the given
/// basic block.
/// \param bl is the given basic block
/// \return \b true if a whole Varnode exists or can be defined before the end of the given basic block
bool SplitVarnode::isWholePhiFeasible(FlowBlock *bl)

{
  if (isConstant()) return false;
  if (!findWholeSplitToPieces()) {
    if (!findWholeBuiltFromPieces()) {
      if (!findDefinitionPoint())
	return false;
    }
  }
  if (defblock == (BlockBasic *)0) return true;
  if (bl == defblock)	// If defined in same block
    return true;
  while(bl != (FlowBlock *)0) { // Make sure defblock dominates block containing -existop-
    bl = bl->getImmedDom();
    if (bl == defblock) return true;
  }
  return false;
}

/// This method assumes isWholeFeasible has been called and returned \b true.
/// If the \b whole didn't already exist, it is created as the concatenation of its two pieces.
/// If the pieces were constant, a constant whole Varnode is created.
/// If the \b hi piece was null, the whole is created as a CPUI_ZEXT of the \b lo.
/// \param data is the function owning the Varnode pieces
void SplitVarnode::findCreateWhole(Funcdata &data)

{
  if (isConstant()) {
    whole = data.newConstant(wholesize,val);
    return;
  }
  else {
    if (lo != (Varnode *)0)
      lo->setPrecisLo();		// Mark the pieces
    if (hi != (Varnode *)0)
      hi->setPrecisHi();
  }

  if (whole != (Varnode *)0) return; // Already found the whole
  PcodeOp *concatop;
  Address addr;
  BlockBasic *topblock = (BlockBasic *)0;

  if (defblock != (BlockBasic *)0)
    addr = defpoint->getAddr();
  else {
    topblock = (BlockBasic *)data.getBasicBlocks().getStartBlock();
    addr = topblock->getStart();
  }

  if (hi != (Varnode*)0) {
    concatop = data.newOp(2,addr);
    // Do we need to pick something other than a unique????
    whole = data.newUniqueOut(wholesize,concatop);
    data.opSetOpcode(concatop,CPUI_PIECE);
    data.opSetOutput(concatop,whole);
    data.opSetInput(concatop,hi,0);
    data.opSetInput(concatop,lo,1);
  }
  else {
    concatop = data.newOp(1,addr);
    whole = data.newUniqueOut(wholesize,concatop);
    data.opSetOpcode(concatop,CPUI_INT_ZEXT);
    data.opSetOutput(concatop,whole);
    data.opSetInput(concatop,lo,0);
  }

  if (defblock != (BlockBasic *)0)
    data.opInsertAfter(concatop,defpoint);
  else
    data.opInsertBegin(concatop,topblock);

  defpoint = concatop;
  defblock = concatop->getParent();
}

/// If the \b whole does not already exist, it is created as a \e unique register.
/// The new Varnode must later be set explicitly as the output of some PcodeOp.
/// \param data is the function owning the Varnode pieces
void SplitVarnode::findCreateOutputWhole(Funcdata &data)

{ // Create the actual -whole- varnode
  lo->setPrecisLo();		// Mark the pieces
  hi->setPrecisHi();
  if (whole != (Varnode *)0) return;
  whole = data.newUnique(wholesize);
}

/// If the pieces can be treated as a contiguous whole, use the same storage location to construct the \b whole,
/// otherwise use a \b join address for storage.
/// \param data is the function owning the pieces
void SplitVarnode::createJoinedWhole(Funcdata &data)

{
  lo->setPrecisLo();
  hi->setPrecisHi();
  if (whole != (Varnode *)0) return;
  Address newaddr;
  if (!isAddrTiedContiguous(lo,hi,newaddr)) {
    newaddr = data.getArch()->constructJoinAddress(data.getArch()->translate,hi->getAddr(),hi->getSize(),
									    lo->getAddr(),lo->getSize());
  }
  whole = data.newVarnode(wholesize,newaddr);
  whole->setWriteMask();
}

/// Assume \b lo was initially defined in some other way but now needs to be defined as a split from
/// a new \b whole Varnode.  The original PcodeOp defining \b lo is transformed into a CPUI_SUBPIECE.
/// The method findCreateOutputWhole() must already have been called on \b this.
void SplitVarnode::buildLoFromWhole(Funcdata &data)

{
  PcodeOp *loop = lo->getDef();
  if (loop == (PcodeOp *)0)
    throw LowlevelError("Building low piece that was originally undefined");

  vector<Varnode *> inlist;
  inlist.push_back(whole);
  inlist.push_back(data.newConstant(4,0));
  if (loop->code() == CPUI_MULTIEQUAL) {
    // When converting the MULTIEQUAL to a SUBPIECE, we need to reinsert the op so that we don't
    // get a break in the sequence of MULTIEQUALs at the beginning of the block
    BlockBasic *bl = loop->getParent();
    data.opUninsert(loop);
    data.opSetOpcode(loop,CPUI_SUBPIECE);
    data.opSetAllInput(loop,inlist);
    data.opInsertBegin(loop,bl);
  }
  else if (loop->code() == CPUI_INDIRECT) {
    // When converting an INDIRECT to a SUBPIECE, we need to reinsert the op AFTER the affector
    PcodeOp *affector = PcodeOp::getOpFromConst(loop->getIn(1)->getAddr());
    if (!affector->isDead())
      data.opUninsert(loop);
    data.opSetOpcode(loop,CPUI_SUBPIECE);
    data.opSetAllInput(loop,inlist);
    if (!affector->isDead())
      data.opInsertAfter(loop,affector);
  }
  else {
    data.opSetOpcode(loop,CPUI_SUBPIECE);
    data.opSetAllInput(loop,inlist);
  }
}

/// Assume \b hi was initially defined in some other way but now needs to be defined as a split from
/// a new \b whole Varnode.  The original PcodeOp defining \b hi is transformed into a CPUI_SUBPIECE.
/// The method findCreateOutputWhole() must already have been called on \b this.
void SplitVarnode::buildHiFromWhole(Funcdata &data)

{
  PcodeOp *hiop = hi->getDef();
  if (hiop == (PcodeOp *)0)
    throw LowlevelError("Building low piece that was originally undefined");

  vector<Varnode *> inlist;
  inlist.push_back(whole);
  inlist.push_back(data.newConstant(4,lo->getSize()));
  if (hiop->code() == CPUI_MULTIEQUAL) {
    // When converting the MULTIEQUAL to a SUBPIECE, we need to reinsert the op so that we don't
    // get a break in the sequence of MULTIEQUALs at the beginning of the block
    BlockBasic *bl = hiop->getParent();
    data.opUninsert(hiop);
    data.opSetOpcode(hiop,CPUI_SUBPIECE);
    data.opSetAllInput(hiop,inlist);
    data.opInsertBegin(hiop,bl);
  }
  else if (hiop->code() == CPUI_INDIRECT) {
    // When converting the INDIRECT to a SUBPIECE, we need to reinsert AFTER the affector
    PcodeOp *affector = PcodeOp::getOpFromConst(hiop->getIn(1)->getAddr());
    if (!affector->isDead())
      data.opUninsert(hiop);
    data.opSetOpcode(hiop,CPUI_SUBPIECE);
    data.opSetAllInput(hiop,inlist);
    if (!affector->isDead())
      data.opInsertAfter(hiop,affector);
  }
  else {
    data.opSetOpcode(hiop,CPUI_SUBPIECE);
    data.opSetAllInput(hiop,inlist);
  }
}

// void SplitVarnode::buildHiFromLoHalf(Funcdata &data,SplitVarnode &oldin,PcodeOp *newwholeop)

// { // Only the lo half of the new logical whole is explicitly constructed, the old input high
//   // is recycled for the hi half of the output, define the new hi half as a SUBPIECE and scan
//   // through the uses of the old hi half to see which should be switch to the new hi half
//   PcodeOp *newhiop = data.newOp(2,newwholeop->getAddr());
//   data.opSetOpcode(newhiop,CPUI_SUBPIECE);
//   data.opSetOutput(newhiop,hi);	// hi was not defined previously
//   data.opSetInput(newhiop,whole,0);
//   data.opSetInput(newhiop,data.newConstant(4,lo->getSize()),1);
//   data.opInsertAfter(newhiop,newwholeop);

//   Varnode *oldhi = oldin.getHi();
//   list<PcodeOp *>::const_iterator iter,enditer;
//   iter = oldhi->beginDescend();
//   enditer = oldhi->endDescend();
//   while(iter != enditer) {
//     PcodeOp *testop = *iter;
//     ++iter;
//     int4 ord = testop->compareOrder(newhiop);
//     if (ord == 1) { 		// newhiop executes earlier than testop
//       int4 slot = testop->getSlot(oldhi);
//       data.opSetInput(testop,hi,slot);
//     }
//   }
// }

/// Its assumed that \b this is the output of the double precision operation being performed.
/// The \b whole Varnode may not yet exist.  This method returns the first PcodeOp where the \b whole
/// needs to exist.  If no such PcodeOp exists, null is returned.
/// \return the first PcodeOp where the \b whole needs to exist or null
PcodeOp *SplitVarnode::findOutExist(void)

{
  if (findWholeBuiltFromPieces()) {
    return defpoint;
  }
  return findEarliestSplitPoint();
}

/// If the logical whole is a constant and is too big to be represented internally return \b true.
/// \return \b true if \b this is a constant and too big
bool SplitVarnode::exceedsConstPrecision(void) const

{
  return isConstant() && (wholesize > sizeof(uintb));
}

/// \brief Check if the values in the given Varnodes differ by the given size
///
/// Return \b true, if the (possibly dynamic) value represented by the given \b vn1 plus \b size1
/// produces the value in the given \b vn2. For constants, the values can be computed directly, but
/// otherwise \b vn1 and \b vn2 must be defined by INT_ADD operations from a common ancestor.
/// \param vn1 is the first given Varnode
/// \param vn2 is the second given Varnode
/// \param size1 is the given size to add to \b vn1
/// \return \b true if the values in \b vn1 and \b vn2 are related by the given size
bool SplitVarnode::adjacentOffsets(Varnode *vn1,Varnode *vn2,uintb size1)

{
  if (vn1->isConstant()) {
    if (!vn2->isConstant()) return false;
    return ((vn1->getOffset() + size1) == vn2->getOffset());
  }

  if (!vn2->isWritten()) return false;
  PcodeOp *op2 = vn2->getDef();
  if (op2->code() != CPUI_INT_ADD) return false;
  if (!op2->getIn(1)->isConstant()) return false;
  uintb c2 = op2->getIn(1)->getOffset();

  if (op2->getIn(0) == vn1)
    return (size1 == c2);

  if (!vn1->isWritten()) return false;
  PcodeOp *op1 = vn1->getDef();
  if (op1->code() != CPUI_INT_ADD) return false;
  if (!op1->getIn(1)->isConstant()) return false;
  uintb c1 = op1->getIn(1)->getOffset();

  if (op1->getIn(0) != op2->getIn(0)) return false;
  return ((c1 + size1) == c2);
}

/// \brief Verify that the pointers into the given LOAD/STORE PcodeOps address contiguous memory
///
/// The two given PcodeOps must either both be LOADs or both be STOREs. The pointer for the
/// first PcodeOp is labeled as the most significant piece of the contiguous whole, the
/// second PcodeOp is labeled as the least significant piece. The p-code defining the pointers is examined
/// to determine if the two memory regions being pointed at really form one contiguous region.
/// If the regions are contiguous and the pointer labeling is valid, \b true is returned, the PcodeOps are sorted
/// into \b first and \b second based on Address, and the address space of the memory region is passed back.
/// \param most is the given LOAD/STORE PcodeOp referring to the most significant region
/// \param least is the given LOAD/STORE PcodeOp referring to the least significant region
/// \param first is used to pass back the earliest of the address sorted PcodeOps
/// \param second is used to pass back the latest of the address sorted PcodeOps
/// \param spc is used to pass back the LOAD address space
/// \param sizeres is used to pass back the combined LOAD size
/// \return true if the given PcodeOps are contiguous LOADs
bool SplitVarnode::testContiguousPointers(PcodeOp *most,PcodeOp *least,PcodeOp *&first,PcodeOp *&second,AddrSpace *&spc)

{
  spc = least->getIn(0)->getSpaceFromConst();
  if (most->getIn(0)->getSpaceFromConst() != spc) return false;

  if (spc->isBigEndian()) {	// Convert significance order to address order
    first = most;
    second = least;
  }
  else {
    first = least;
    second = most;
  }
  Varnode *firstptr = first->getIn(1);
  if (firstptr->isFree()) return false;
  int4 sizeres;
  if (first->code() == CPUI_LOAD)
    sizeres = first->getOut()->getSize(); // # of bytes read by lowest address load
  else		// CPUI_STORE
    sizeres = first->getIn(2)->getSize();

  // Check if the loads are adjacent to each other
  return adjacentOffsets(first->getIn(1),second->getIn(1),(uintb)sizeres);
}

/// \brief Return \b true if the given pieces can be melded into a contiguous storage location
///
/// The given Varnodes must be \e address \e tied, and their storage must line up, respecting their
/// significance as pieces.
/// \param lo is the given least significant piece
/// \param hi is the given most significant piece
/// \param res is used to pass back the starting address of the contigous range
/// \return \b true if the pieces are address tied and form a contiguous range
bool SplitVarnode::isAddrTiedContiguous(Varnode *lo,Varnode *hi,Address &res)

{
  if (!lo->isAddrTied()) return false;
  if (!hi->isAddrTied()) return false;

  // Make sure there is no explicit symbol that would prevent the pieces from being joined
  SymbolEntry *entryLo = lo->getSymbolEntry();
  SymbolEntry *entryHi = hi->getSymbolEntry();
  if (entryLo != (SymbolEntry *)0 || entryHi != (SymbolEntry *)0) {
    if (entryLo == (SymbolEntry *)0 || entryHi == (SymbolEntry *)0)
      return false;		// One is marked with a symbol, the other is not
    if (entryLo->getSymbol() != entryHi->getSymbol())
      return false;		// They are part of different symbols
  }
  AddrSpace *spc = lo->getSpace();
  if (spc != hi->getSpace()) return false;
  uintb looffset = lo->getOffset();
  uintb hioffset = hi->getOffset();
  if (spc->isBigEndian()) {
    if (hioffset >= looffset) return false;
    if (hioffset + hi->getSize() != looffset) return false;
    res = hi->getAddr();
  }
  else {
    if (looffset >= hioffset) return false;
    if (looffset + lo->getSize() != hioffset) return false;
    res = lo->getAddr();
  }
  return true;
}

/// \brief Create a list of all the possible pairs that contain the same logical value as the given Varnode
///
/// The given Varnode is assumed to be the logical whole that is being used in a double precision calculation.
/// At least one of the most or least significant pieces must be extracted from the whole and must be
/// marked as a double precision piece.
/// \param w is the given Varnode whole
/// \param splitvec is the container for holding any discovered SplitVarnodes
void SplitVarnode::wholeList(Varnode *w,vector<SplitVarnode> &splitvec)

{
  SplitVarnode basic;

  basic.whole = w;
  basic.hi = (Varnode *)0;
  basic.lo = (Varnode *)0;
  basic.wholesize = w->getSize();
  list<PcodeOp *>::const_iterator iter,enditer;

  iter = basic.whole->beginDescend();
  enditer = basic.whole->endDescend();
  int4 res = 0;
  while(iter != enditer) {
    PcodeOp *subop = *iter;
    ++iter;
    if (subop->code() != CPUI_SUBPIECE) continue;
    Varnode *vn = subop->getOut();
    if (vn->isPrecisHi()) {
      if (subop->getIn(1)->getOffset() != basic.wholesize - vn->getSize()) continue;
      basic.hi = vn;
      res |= 2;
    }
    else if (vn->isPrecisLo()) {
      if (subop->getIn(1)->getOffset() != 0) continue;
      basic.lo = vn;
      res |= 1;
    }
  }
  if (res==0) return;
  if (res == 3 && (basic.lo->getSize() + basic.hi->getSize() != basic.wholesize))
    return;

  splitvec.push_back(basic);
  findCopies(basic,splitvec);
}

/// \brief Find copies from (the pieces of) the given SplitVarnode
///
/// Scan for each piece being used as input to COPY operations.  If the each piece is
/// copied within the same basic block to contiguous storage locations, create a new
/// SplitVarnode from COPY outputs and add it to the list.
/// \param in is the given SplitVarnode
/// \param splitvec is the container for holding SplitVarnode copies
void SplitVarnode::findCopies(const SplitVarnode &in,vector<SplitVarnode> &splitvec)

{
  if (!in.hasBothPieces()) return;
  list<PcodeOp *>::const_iterator iter,enditer;

  iter = in.getLo()->beginDescend();
  enditer = in.getLo()->endDescend();
  while(iter != enditer) {
    PcodeOp *loop = *iter;
    ++iter;
    if (loop->code() != CPUI_COPY) continue;
    Varnode *locpy = loop->getOut();
    Address addr = locpy->getAddr(); // Calculate address of hi part
    if (addr.isBigEndian())
      addr = addr - (in.getHi()->getSize());
    else
      addr = addr + locpy->getSize();
    list<PcodeOp *>::const_iterator iter2,enditer2;
    iter2 = in.getHi()->beginDescend();
    enditer2 = in.getHi()->endDescend();
    while(iter2 != enditer2) {
      PcodeOp *hiop = *iter2;
      ++iter2;
      if (hiop->code() != CPUI_COPY) continue;
      Varnode *hicpy = hiop->getOut();
      if (hicpy->getAddr() != addr) continue;
      if (hiop->getParent() != loop->getParent()) continue;
      SplitVarnode newsplit;
      newsplit.initAll(in.getWhole(),locpy,hicpy);
      splitvec.push_back(newsplit);
    }
  }
}

/// \brief For the given CBRANCH PcodeOp, pass back the \b true and \b false basic blocks
///
/// The result depends on the \e boolean \e flip property of the CBRANCH, and the user can
/// also flip the meaning of the branches.
/// \param boolop is the given CBRANCH PcodeOp
/// \param flip is \b true if the caller wants to flip the meaning of the blocks
/// \param trueout is used to pass back the true fall-through block
/// \param falseout is used to pass back the false fall-through block
void SplitVarnode::getTrueFalse(PcodeOp *boolop,bool flip,BlockBasic *&trueout,BlockBasic *&falseout)

{
  BlockBasic *parent = boolop->getParent();
  BlockBasic *trueblock = (BlockBasic *)parent->getTrueOut();
  BlockBasic *falseblock =(BlockBasic *)parent->getFalseOut();
  if (boolop->isBooleanFlip() != flip) {
    trueout = falseblock;
    falseout = trueblock;
  }
  else {
    trueout = trueblock;
    falseout = falseblock;
  }
}

/// \brief Return \b true if the basic block containing the given CBRANCH PcodeOp performs no other operation.
///
/// The basic block can contain the CBRANCH and the one PcodeOp producing the boolean value.
/// Otherwise \b false is returned.
/// \param branchop is the given CBRANCH
/// \return \b true if the parent basic block performs only the branch operation
bool SplitVarnode::otherwiseEmpty(PcodeOp *branchop)

{
  BlockBasic *bl = branchop->getParent();
  if (bl->sizeIn() != 1) return false;
  PcodeOp *otherop = (PcodeOp *)0;
  Varnode *vn = branchop->getIn(1);
  if (vn->isWritten())
    otherop = vn->getDef();
  list<PcodeOp *>::const_iterator iter,enditer;
  iter = bl->beginOp();
  enditer = bl->endOp();
  while(iter != enditer) {
    PcodeOp *op = *iter;
    ++iter;
    if (op == otherop) continue;
    if (op == branchop) continue;
    return false;
  }
  return true;
}

/// \brief Verify that the given PcodeOp is a CPUI_INT_MULT by -1
///
/// The PcodeOp must be a CPUI_INT_MULT and the second operand must be a constant -1.
/// \param op is the given PcodeOp
/// \return \b true if the PcodeOp is a multiple by -1
bool SplitVarnode::verifyMultNegOne(PcodeOp *op)

{
  if (op->code() != CPUI_INT_MULT) return false;
  Varnode *in1 = op->getIn(1);
  if (!in1->isConstant()) return false;
  if (in1->getOffset() != calc_mask(in1->getSize())) return false;
  return true;
}

/// \brief Check that the logical version of a (generic) binary double-precision operation can be created
///
/// This checks only the most generic aspects of the calculation.  The input and output whole Varnodes
/// must already exist or be creatable.  The point where the output Varnode must exist is identified
/// and returned.  If the binary operation cannot be created, null is returned.
/// \param out is the output of the binary operation
/// \param in1 is the first input of the binary operation
/// \param in2 is the second input of the binary operation
/// \return the first PcodeOp where the output whole must exist
PcodeOp *SplitVarnode::prepareBinaryOp(SplitVarnode &out,SplitVarnode &in1,SplitVarnode &in2)

{
  PcodeOp *existop = out.findOutExist(); // Find point where output whole needs to exist
  if (existop == (PcodeOp *)0) return existop; // If we can find no such point return false;
  if (!in1.isWholeFeasible(existop)) return (PcodeOp *)0;
  if (!in2.isWholeFeasible(existop)) return (PcodeOp *)0;
  return existop;
}

/// \brief Rewrite a double precision binary operation by replacing the pieces with unified Varnodes
///
/// This assumes that we have checked that the transformation is possible via the various
/// verify and prepare methods.  After this method is called, the logical inputs and output of
/// the calculation will exist as real Varnodes.
/// \param data is the function owning the operation
/// \param out is the output of the binary operation
/// \param in1 is the first input to the binary operation
/// \param in2 is the second input to the binary operation
/// \param existop is the precalculated PcodeOp where the output whole Varnode must exist
/// \param opc is the opcode of the operation
void SplitVarnode::createBinaryOp(Funcdata &data,SplitVarnode &out,SplitVarnode &in1,SplitVarnode &in2,
				  PcodeOp *existop,OpCode opc)

{
  out.findCreateOutputWhole(data);
  in1.findCreateWhole(data);
  in2.findCreateWhole(data);
  if (existop->code() != CPUI_PIECE) { // If the output whole didn't previously exist
    PcodeOp *newop = data.newOp(2,existop->getAddr()); // new op which creates the output whole
    data.opSetOpcode(newop,opc);
    data.opSetOutput(newop,out.getWhole());
    data.opSetInput(newop,in1.getWhole(),0);
    data.opSetInput(newop,in2.getWhole(),1);
    data.opInsertBefore(newop,existop);
    out.buildLoFromWhole(data);
    out.buildHiFromWhole(data);
  }
  else {			// The whole previously existed
    data.opSetOpcode(existop,opc); // our new op replaces the op previously defining the output whole
    data.opSetInput(existop,in1.getWhole(),0);
    data.opSetInput(existop,in2.getWhole(),1);
  }
}

/// \brief Make sure input and output operands of a double precision shift operation are compatible
///
/// Do generic testing that the input and output whole Varnodes can be created.  Calculate the
/// PcodeOp where the output whole must exist and return it.  If logical operation cannot be created,
/// return null.
/// \param out is the output of the double precision shift operation
/// \param in is the (first) input operand of the double precision shift operation
/// \return the PcodeOp where output whole must exist or null
PcodeOp *SplitVarnode::prepareShiftOp(SplitVarnode &out,SplitVarnode &in)

{
  PcodeOp *existop = out.findOutExist(); // Find point where output whole needs to exist
  if (existop == (PcodeOp *)0) return existop;
  if (!in.isWholeFeasible(existop)) return (PcodeOp *)0;
  return existop;
}

/// \brief Rewrite a double precision shift by replacing hi/lo pieces with unified Varnodes
///
/// This assumes that we have checked that the transformation is possible by calling the appropriate
/// verify and prepare methods. After this method is called, the logical inputs and output of
/// the calculation will exist as real Varnodes.  The \e shift \e amount is not treated as a double
/// precision variable.
/// \param data is the function owning the operation
/// \param out is the output of the double precision operation
/// \param in is the first input of the operation
/// \param sa is the Varnode indicating the \e shift \e amount for the operation
/// \param existop is the first PcodeOp where the output whole needs to exist
/// \param opc is the opcode of the particular shift operation
void SplitVarnode::createShiftOp(Funcdata &data,SplitVarnode &out,SplitVarnode &in,Varnode *sa,
				 PcodeOp *existop,OpCode opc)

{
  out.findCreateOutputWhole(data);
  in.findCreateWhole(data);
  if (sa->isConstant())
    sa = data.newConstant(sa->getSize(),sa->getOffset());
  if (existop->code() != CPUI_PIECE) { // If the output whole didn't previously exist
    PcodeOp *newop = data.newOp(2,existop->getAddr());
    data.opSetOpcode(newop,opc);
    data.opSetOutput(newop,out.getWhole());
    data.opSetInput(newop,in.getWhole(),0);
    data.opSetInput(newop,sa,1);
    data.opInsertBefore(newop,existop);
    out.buildLoFromWhole(data);
    out.buildHiFromWhole(data);
  }
  else {			// The whole previously existed, we remake the defining op
    data.opSetOpcode(existop,opc);
    data.opSetInput(existop,in.getWhole(),0);
    data.opSetInput(existop,sa,1);
  }
}

/// \brief Try to perform one transform on a logical double precision operation given a specific input
///
/// All the various double precision forms are lined up against the input.  The first one that matches
/// has its associated transform performed and then 1 is returned.  If no form matches, 0 is returned.
/// \param in is the given double precision input
/// \param data is the function owning the Varnodes
/// \return a count of the number of transforms applied, 0 or 1
int4 SplitVarnode::applyRuleIn(SplitVarnode &in,Funcdata &data)

{
  for(int4 i=0;i<2;++i) {
    Varnode *vn;
    vn = (i==0) ? in.getHi() : in.getLo();
    if (vn == (Varnode *)0) continue;
    bool workishi = (i==0);
    list<PcodeOp *>::const_iterator iter,enditer;
    iter = vn->beginDescend();
    enditer = vn->endDescend();
    while(iter != enditer) {
      PcodeOp *workop = *iter;
      ++iter;
      switch(workop->code()) {
      case CPUI_INT_ADD:
	{
	  AddForm addform;
	  if (addform.applyRule(in,workop,workishi,data))
	    return 1;
	  SubForm subform;
	  if (subform.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_AND:
	{
	  Equal3Form equal3form;
	  if (equal3form.applyRule(in,workop,workishi,data))
	    return 1;
	  LogicalForm logicalform;
	  if (logicalform.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_OR:
	{
	  LogicalForm logicalform;
	  if (logicalform.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_XOR:
	{
	  LogicalForm logicalform;
	  if (logicalform.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_EQUAL:
      case CPUI_INT_NOTEQUAL:
	{
	  LessThreeWay lessthreeway;
	  if (lessthreeway.applyRule(in,workop,workishi,data))
	    return 1;
	  Equal1Form equal1form;
	  if (equal1form.applyRule(in,workop,workishi,data))
	    return 1;
	  Equal2Form equal2form;
	  if (equal2form.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_LESS:
      case CPUI_INT_LESSEQUAL:
	{
	  LessThreeWay lessthreeway;
	  if (lessthreeway.applyRule(in,workop,workishi,data))
	    return 1;
	  LessConstForm lessconstform;
	  if (lessconstform.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_SLESS:
	{
	  LessConstForm lessconstform;
	  if (lessconstform.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_SLESSEQUAL:
	{
	  LessConstForm lessconstform;
	  if (lessconstform.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_LEFT:
	{
	  ShiftForm shiftform;
	  if (shiftform.applyRuleLeft(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_RIGHT:
	{
	  ShiftForm shiftform;
	  if (shiftform.applyRuleRight(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_SRIGHT:
	{
	  ShiftForm shiftform;
	  if (shiftform.applyRuleRight(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INT_MULT:
	{
	  MultForm multform;
	  if (multform.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_MULTIEQUAL:
	{
	  PhiForm phiform;
	  if (phiform.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_INDIRECT:
	{
	  IndirectForm indform;
	  if (indform.applyRule(in,workop,workishi,data))
	    return 1;
	}
	break;
      case CPUI_COPY:
	if (workop->getOut()->isAddrForce()) {
	  CopyForceForm copyform;
	  if (copyform.applyRule(in, workop, workishi, data))
	    return 1;
	}
	break;
      default:
	break;
      }
    }
  }
  return 0;
}

/// \brief Make sure input operands of a double precision compare operation are compatible
///
/// Do generic testing that the input whole Varnodes can be created.  If they can be created, return \b true.
/// \param in1 is the first input operand of the double precision compare operation
/// \param in2 is the second input operand of the double precision compare operation
/// \return \b true if the logical transformation can be performed
bool SplitVarnode::prepareBoolOp(SplitVarnode &in1,SplitVarnode &in2,PcodeOp *testop)

{
  if (!in1.isWholeFeasible(testop)) return false;
  if (!in2.isWholeFeasible(testop)) return false;
  return true;
}

/// \brief Rewrite a double precision boolean operation by replacing the input pieces with unified Varnodes
///
/// This assumes we checked that the transformation is possible by calling the various verify and prepare
/// methods. The inputs to the given PcodeOp producing the final boolean value are replaced with new
/// logical Varnodes, and the opcode is updated.  The output Varnode is not affected.
/// \param data is the function owning the operation
/// \param boolop is the given PcodeOp producing the final boolean value
/// \param in1 is the first input to the operation
/// \param in2 is the second input to the operation
/// \param opc is the opcode of the operation
void SplitVarnode::replaceBoolOp(Funcdata &data,PcodeOp *boolop,SplitVarnode &in1,SplitVarnode &in2,
				 OpCode opc)

{
  in1.findCreateWhole(data);
  in2.findCreateWhole(data);
  data.opSetOpcode(boolop,opc);
  data.opSetInput(boolop,in1.getWhole(),0);
  data.opSetInput(boolop,in2.getWhole(),1);
}

/// \brief Create a new compare PcodeOp, replacing the boolean Varnode taken as input by the given CBRANCH
///
/// The inputs to the new compare operation are Varnodes representing the logical whole of the double precision
/// pieces.
/// \param data is the function owning the operation
/// \param cbranch is the given CBRANCH PcodeOp
/// \param in1 is the first input to the compare operation
/// \param in2 is the second input to the compare operation
/// \param opc is the opcode of the compare operation
void SplitVarnode::createBoolOp(Funcdata &data,PcodeOp *cbranch,SplitVarnode &in1,SplitVarnode &in2,
				OpCode opc)

{
  PcodeOp *addrop = cbranch;
  Varnode *boolvn = cbranch->getIn(1);
  if (boolvn->isWritten())
    addrop = boolvn->getDef();	// Use the address of the comparison operator
  in1.findCreateWhole(data);
  in2.findCreateWhole(data);
  PcodeOp *newop = data.newOp(2,addrop->getAddr());
  data.opSetOpcode(newop,opc);
  Varnode *newbool = data.newUniqueOut(1,newop);
  data.opSetInput(newop,in1.getWhole(),0);
  data.opSetInput(newop,in2.getWhole(),1);
  data.opInsertBefore(newop,cbranch);
  data.opSetInput(cbranch,newbool,1); // CBRANCH now determined by new compare
}

/// \brief Check that the logical version of a CPUI_MULTIEQUAL operation can be created
///
/// This checks only the most generic aspects of the calculation.  The input and output whole Varnodes
/// must already exist or be creatable.  The point where the output Varnode must exist is identified
/// and returned.  If the MULTIEQUAL operation cannot be created, null is returned.
/// \param out is the output of the MULTIEQUAL operation
/// \param inlist is a vector of the input operands to the MULTIEQUAL
/// \return the first PcodeOp where the output whole must exist
PcodeOp *SplitVarnode::preparePhiOp(SplitVarnode &out,vector<SplitVarnode> &inlist)

{
  PcodeOp *existop = out.findEarliestSplitPoint(); // Point where output whole needs to exist
  if (existop == (PcodeOp *)0) return existop;
  // existop should always be a MULTIEQUAL defining one of the pieces
  if (existop->code() != CPUI_MULTIEQUAL)
    throw LowlevelError("Trying to create phi-node double precision op with phi-node pieces");
  BlockBasic *bl = existop->getParent();
  int4 numin = inlist.size();
  for(int4 i=0;i<numin;++i)
    if (!inlist[i].isWholePhiFeasible(bl->getIn(i)))
      return (PcodeOp *)0;
  return existop;
}

/// \brief Rewrite a double precision MULTIEQUAL operation by replacing the pieces with unified Varnodes
///
/// This assumes that we have checked that the transformation is possible via the various
/// verify and prepare methods.  After this method is called, the logical inputs and output of
/// the calculation will exist as real Varnodes.
/// \param data is the function owning the operation
/// \param out is the output of the MULTIEQUAL operation
/// \param inlist is the list of input operands to the MULTIEQUAL
/// \param existop is the precalculated PcodeOp where the output whole Varnode must exist
void SplitVarnode::createPhiOp(Funcdata &data,SplitVarnode &out,vector<SplitVarnode> &inlist,
			       PcodeOp  *existop)

{
  // Unlike replaceBoolOp, we MUST create a newop even if the output whole already exists
  // because the MULTIEQUAL has a lot of placement constraints on it
  out.findCreateOutputWhole(data);
  int4 numin = inlist.size();
  for(int4 i=0;i<numin;++i)
    inlist[i].findCreateWhole(data);

  PcodeOp *newop = data.newOp(numin,existop->getAddr());
  data.opSetOpcode(newop,CPUI_MULTIEQUAL);
  data.opSetOutput(newop,out.getWhole());
  for(int4 i=0;i<numin;++i)
    data.opSetInput(newop,inlist[i].getWhole(),i);
  data.opInsertBefore(newop,existop);
  out.buildLoFromWhole(data);
  out.buildHiFromWhole(data);
}

/// \brief Check that the logical version of a CPUI_INDIRECT operation can be created
///
/// This checks only the most generic aspects of the calculation.  The input whole Varnode
/// must already exist or be creatable.  If the INDIRECT operation cannot be created, \b false is returned.
/// \param in is the (first) input operand of the INDIRECT
/// \return \b true if the logical version of the CPUI_INDIRECT can be created
bool SplitVarnode::prepareIndirectOp(SplitVarnode &in,PcodeOp *affector)

{
  // We already have the exist point, -indop-
  if (!in.isWholeFeasible(affector))
    return false;
  return true;
}

/// \brief Rewrite a double precision INDIRECT operation by replacing the pieces with unified Varnodes
///
/// This assumes that we have checked that the transformation is possible via the various
/// verify and prepare methods.  After this method is called, the logical input and output of
/// the calculation will exist as real Varnodes.
/// \param data is the function owning the operation
/// \param out is the output of the INDIRECT operation
/// \param in is the (first) operand of the INDIRECT
/// \param affector is the second operand to the indirect, the PcodeOp producing the indirect affect
void SplitVarnode::replaceIndirectOp(Funcdata &data,SplitVarnode &out,SplitVarnode &in,PcodeOp *affector)

{
  out.createJoinedWhole(data);

  in.findCreateWhole(data);
  PcodeOp *newop = data.newOp(2,affector->getAddr());
  data.opSetOpcode(newop,CPUI_INDIRECT);
  data.opSetOutput(newop,out.getWhole());
  data.opSetInput(newop,in.getWhole(),0);
  data.opSetInput(newop,data.newVarnodeIop(affector),1);
  data.opInsertBefore(newop,affector);
  out.buildLoFromWhole(data);
  out.buildHiFromWhole(data);
}

/// \brief Rewrite the double precision version of a COPY to an address forced Varnode
///
/// This assumes that we have checked that the transformation is possible.  The logical input must already
/// exist, and after this method is called, the logical output will also exist.  The original COPY pieces
/// are explicitly destroyed.
/// \param data is the function owning the COPYs
/// \param addr is the storage address being COPYed
/// \param in is the input to the COPYs
/// \param copylo is the original least significant COPY
/// \param copyhi is the original most significant COPY
void SplitVarnode::replaceCopyForce(Funcdata &data,const Address &addr,SplitVarnode &in,PcodeOp *copylo,PcodeOp *copyhi)

{
  Varnode *inVn = in.getWhole();
  bool returnForm = copyhi->isReturnCopy();
  if (returnForm && inVn->getAddr() != addr) {
    // Placeholder for global propagation past a RETURN needs an additional COPY
    PcodeOp *otherPoint1 = copyhi->getIn(0)->getDef();
    PcodeOp *otherPoint2 = copylo->getIn(0)->getDef();
    // We know these are COPYs in the same basic block.  Compute the later one.
    if (otherPoint1->getSeqNum().getOrder() < otherPoint2->getSeqNum().getOrder())
      otherPoint1 = otherPoint2;
    PcodeOp *otherCopy = data.newOp(1, otherPoint1->getAddr());
    data.opSetOpcode(otherCopy, CPUI_COPY);
    Varnode *vn = data.newVarnodeOut(in.getSize(), addr, otherCopy);
    data.opSetInput(otherCopy,inVn,0);
    data.opInsertBefore(otherCopy, otherPoint1);
    inVn = vn;
  }
  PcodeOp *wholeCopy = data.newOp(1, copyhi->getAddr());
  data.opSetOpcode(wholeCopy, CPUI_COPY);
  Varnode *outVn = data.newVarnodeOut(in.getSize(), addr, wholeCopy);
  outVn->setAddrForce();
  if (returnForm)
    data.markReturnCopy(wholeCopy);
  data.opSetInput(wholeCopy,inVn,0);
  data.opInsertBefore(wholeCopy, copyhi);
  data.opDestroy(copyhi);	// Destroy the original COPYs.  Outputs have no descendants.
  data.opDestroy(copylo);
}

bool AddForm::checkForCarry(PcodeOp *op)

{ // If -op- matches a CARRY construction based on lo1 (i.e. CARRY(x,lo1) )
  //    set lo1 (and negconst if lo1 is a constant) to be the corresponding part of the carry
  //    and return true
  if (op->code() != CPUI_INT_ZEXT) return false;
  if (!op->getIn(0)->isWritten()) return false;
  
  PcodeOp *carryop = op->getIn(0)->getDef();
  if (carryop->code() == CPUI_INT_CARRY) { // Normal CARRY form
    if (carryop->getIn(0) == lo1)
      lo2 = carryop->getIn(1);
    else if (carryop->getIn(1) == lo1)
      lo2 = carryop->getIn(0);
    else
      return false;
    if (lo2->isConstant()) return false;
    return true;
  }
  if (carryop->code() == CPUI_INT_LESS) { // Possible CARRY
    Varnode *tmpvn = carryop->getIn(0);
    if (tmpvn->isConstant()) {
      if (carryop->getIn(1) != lo1) return false;
      negconst = tmpvn->getOffset();
      // In constant forms, the <= will get converted to a <
      // Note the lessthan to less conversion adds 1 then the 2's complement subtracts 1 and negates
      // So all we really need to do is negate
      negconst = (~negconst) & calc_mask(lo1->getSize());
      lo2 = (Varnode *)0;
      return true;
    }
    else if (tmpvn->isWritten()) {	// Calculate CARRY relative to result of loadd
      PcodeOp *loadd_op = tmpvn->getDef();	// This is the putative loadd
      if (loadd_op->code() != CPUI_INT_ADD) return false;
      Varnode *othervn;
      if (loadd_op->getIn(0)==lo1)
	othervn = loadd_op->getIn(1);
      else if (loadd_op->getIn(1)==lo1)
	othervn = loadd_op->getIn(0);
      else
	return false;			// One side of the add must be lo1
      if (othervn->isConstant()) {
	negconst = othervn->getOffset();
	lo2 = (Varnode *)0;
	Varnode *relvn = carryop->getIn(1);
	if (relvn == lo1) return true;	// Comparison can be relative to lo1
	if (!relvn->isConstant()) return false;
	if (relvn->getOffset() != negconst) return false;	// Otherwise must be relative to (constant)lo2
	return true;
      }
      else {
	lo2 = othervn;		// Other side of putative loadd must be lo2
	Varnode *compvn = carryop->getIn(1);
	if ((compvn == lo2)||(compvn == lo1))
	  return true;
      }
    }
    return false;
  }
  if (carryop->code() == CPUI_INT_NOTEQUAL) { // Possible CARRY against -1
    if (!carryop->getIn(1)->isConstant()) return false;
    if (carryop->getIn(0) != lo1) return false;
    if (carryop->getIn(1)->getOffset() != 0) return false;
    negconst = calc_mask(lo1->getSize()); // Original CARRY constant must have been -1
    lo2 = (Varnode *)0;
    return true;
  }
  return false;
}

// Given a known double precision input, look for a double precision add,
// recovering the other double input and the double output
//
// Assume hi1, lo1 is a known double precision pair, we look for
//   reshi = hi1 + hi2 + hizext             (2 variants here)
//   hizext = zext(bool)
//   {                                       (2 variants)
//     bool = (-lo1 <= lo2)     OR
//     bool = (-lo2 <= lo1)                  (multiple ways to calculate negation)
//   }
//   reslo = lo1 + lo2

bool AddForm::verify(Varnode *h,Varnode *l,PcodeOp *op)

{
  hi1 = h;
  lo1 = l;
  slot1 = op->getSlot(hi1);
  for(int4 i=0;i<3;++i) {
    if (i==0) {		// Assume we have to descend one more add
      add2 = op->getOut()->loneDescend();
      if (add2 == (PcodeOp *)0) continue;
      if (add2->code() != CPUI_INT_ADD) continue;
      reshi = add2->getOut();
      hizext1 = op->getIn(1-slot1);
      hizext2 = add2->getIn(1-add2->getSlot(op->getOut()));
    }
    else if (i==1) {		// Assume we are at the bottom most of two adds
      Varnode *tmpvn = op->getIn(1-slot1);
      if (!tmpvn->isWritten()) continue;
      add2 = tmpvn->getDef();
      if (add2->code() != CPUI_INT_ADD) continue;
      reshi = op->getOut();
      hizext1 = add2->getIn(0);
      hizext2 = add2->getIn(1);
    }
    else {			// Assume there is only one add, with second implied add by 0
      reshi = op->getOut();
      hizext1 = op->getIn(1-slot1);
      hizext2 = (Varnode *)0;
    }
    for(int4 j=0;j<2;++j) {
      if (i==2) {		// hi2 is an implied 0
	if (!hizext1->isWritten()) continue;
	zextop = hizext1->getDef();
	hi2 = (Varnode *)0;
      }
      else if (j==0) {
	if (!hizext1->isWritten()) continue;
	zextop = hizext1->getDef();
	hi2 = hizext2;
      }
      else {
	if (!hizext2->isWritten()) continue;
	zextop = hizext2->getDef();
	hi2 = hizext1;
      }
      if (!checkForCarry(zextop)) continue; // Calculate lo2 and negconst

      list<PcodeOp *>::const_iterator iter2,enditer2;
      iter2 = lo1->beginDescend();
      enditer2 = lo1->endDescend();
      while(iter2 != enditer2) {
	loadd = *iter2;
	++iter2;
	if (loadd->code() != CPUI_INT_ADD) continue;
	Varnode *tmpvn = loadd->getIn(1-loadd->getSlot(lo1));
	if (lo2 == (Varnode *)0) {
	  if (!tmpvn->isConstant()) continue;
	  if (tmpvn->getOffset() != negconst) continue;	// Must add same constant used to calculate CARRY
	  lo2 = tmpvn;
	}
	else if (lo2->isConstant()) {
	  if (!tmpvn->isConstant()) continue;
	  if (lo2->getOffset() != tmpvn->getOffset()) continue;
	}
	else if (loadd->getIn(1-loadd->getSlot(lo1)) != lo2) // Must add same value used to calculate CARRY
	  continue;
	reslo = loadd->getOut();
	return true;
      }
    }
  }
  return false;
}

bool AddForm::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;
  if (!verify(in.getHi(),in.getLo(),op))
    return false;

  indoub.initPartial(in.getSize(),lo2,hi2);
  if (indoub.exceedsConstPrecision())
    return false;
  outdoub.initPartial(in.getSize(),reslo,reshi);
  existop = SplitVarnode::prepareBinaryOp(outdoub,in,indoub);
  if (existop == (PcodeOp *)0)
    return false;
  SplitVarnode::createBinaryOp(data,outdoub,in,indoub,existop,CPUI_INT_ADD);
  return true;
}

// Given a known double precision input, look for a double precision subtraction,
// recovering the other double input and the double output
//
// Assume hi1, lo1 is a known double precision pair, we look for
//   reshi = hi1 + -hi2 + - zext(lo1 < lo2)
//   reslo = lo1 + -lo2

bool SubForm::verify(Varnode *h,Varnode *l,PcodeOp *op)

{
  list<PcodeOp *>::const_iterator iter2,enditer2;
  hi1 = h;
  lo1 = l;
  slot1 = op->getSlot(hi1);
  for(int4 i=0;i<2;++i) {
    if (i==0) {		// Assume we have to descend one more add
      add2 = op->getOut()->loneDescend();
      if (add2 == (PcodeOp *)0) continue;
      if (add2->code() != CPUI_INT_ADD) continue;
      reshi = add2->getOut();
      hineg1 = op->getIn(1-slot1);
      hineg2 = add2->getIn(1-add2->getSlot(op->getOut()));
    }
    else {
      Varnode *tmpvn = op->getIn(1-slot1);
      if (!tmpvn->isWritten()) continue;
      add2 = tmpvn->getDef();
      if (add2->code() != CPUI_INT_ADD) continue;
      reshi = op->getOut();
      hineg1 = add2->getIn(0);
      hineg2 = add2->getIn(1);
    }
    if (!hineg1->isWritten()) continue;
    if (!hineg2->isWritten()) continue;
    if (!SplitVarnode::verifyMultNegOne(hineg1->getDef())) continue;
    if (!SplitVarnode::verifyMultNegOne(hineg2->getDef())) continue;
    hizext1 = hineg1->getDef()->getIn(0);
    hizext2 = hineg2->getDef()->getIn(0);
    for(int4 j=0;j<2;++j) {
      if (j==0) {
	if (!hizext1->isWritten()) continue;
	zextop = hizext1->getDef();
	hi2 = hizext2;
      }
      else {
	if (!hizext2->isWritten()) continue;
	zextop = hizext2->getDef();
	hi2 = hizext1;
      }
      if (zextop->code() != CPUI_INT_ZEXT) continue;
      if (!zextop->getIn(0)->isWritten()) continue;
      lessop = zextop->getIn(0)->getDef();
      if (lessop->code() != CPUI_INT_LESS) continue;
      if (lessop->getIn(0) != lo1) continue;
      lo2 = lessop->getIn(1);
      iter2 = lo1->beginDescend();
      enditer2 = lo1->endDescend();
      while(iter2 != enditer2) {
	loadd = *iter2;
	++iter2;
	if (loadd->code() != CPUI_INT_ADD) continue;
	Varnode *tmpvn = loadd->getIn(1-loadd->getSlot(lo1));
	if (!tmpvn->isWritten()) continue;
	negop = tmpvn->getDef();
	if (!SplitVarnode::verifyMultNegOne(negop)) continue;
	if (negop->getIn(0) != lo2) continue;
	reslo = loadd->getOut();
	return true;
      }
    }
  }
  return false;
}

bool SubForm::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;

  if (!verify(in.getHi(),in.getLo(),op))
    return false;

  indoub.initPartial(in.getSize(),lo2,hi2);
  if (indoub.exceedsConstPrecision())
    return false;
  outdoub.initPartial(in.getSize(),reslo,reshi);
  existop = SplitVarnode::prepareBinaryOp(outdoub,in,indoub);
  if (existop == (PcodeOp *)0)
    return false;
  SplitVarnode::createBinaryOp(data,outdoub,in,indoub,existop,CPUI_INT_SUB);
  return true;
}

int4 LogicalForm::findHiMatch(void)

{ // Look for the op computing the most significant part of the result for which -loop- computes
  // the least significant part,  look for a known double precis out, then look for known double
  // precis in.  If the other input is constant, look for a unique op that might be computing the high,
  // Return 0 if we found an op, return -1, if we can't find an op, return -2 if no op exists
  Varnode *lo1Tmp = in.getLo();
  Varnode *vn2 = loop->getIn( 1-loop->getSlot( lo1Tmp ) );
  
  SplitVarnode out;
  if (out.inHandLoOut(lo1Tmp)) {	// If we already know what the double precision output looks like
    Varnode *hi = out.getHi();
    if (hi->isWritten()) {	// Just look at construction of hi precisi
      PcodeOp *maybeop = hi->getDef();
      if (maybeop->code() == loop->code()) {
	if (maybeop->getIn(0) == hi1) {
	  if (maybeop->getIn(1)->isConstant() == vn2->isConstant()) {
	    hiop = maybeop;
	    return 0;
	  }
	}
	else if (maybeop->getIn(1) == hi1) {
	  if (maybeop->getIn(0)->isConstant() == vn2->isConstant()) {
	    hiop = maybeop;
	    return 0;
	  }
	}
      }
    }
  }

  if (!vn2->isConstant()) {
    SplitVarnode in2;
    if (in2.inHandLo(vn2)) {	// If we already know what the other double precision input looks like
      list<PcodeOp *>::const_iterator iter,enditer;
      iter = in2.getHi()->beginDescend();
      enditer = in2.getHi()->endDescend();
      while(iter != enditer) {
	PcodeOp *maybeop = *iter;
	++iter;
	if (maybeop->code() == loop->code()) {
	  if ((maybeop->getIn(0) == hi1)||(maybeop->getIn(1) == hi1)) {
	    hiop = maybeop;
	    return 0;
	  }
	}
      }
    }
    return -1;
  }
  else {
    list<PcodeOp *>::const_iterator iter,enditer;
    iter = hi1->beginDescend();
    enditer = hi1->endDescend();
    int4 count = 0;
    PcodeOp *lastop = (PcodeOp *)0;
    while(iter != enditer) {
      PcodeOp *maybeop = *iter;
      ++iter;
      if (maybeop->code() == loop->code()) {
	if (maybeop->getIn(1)->isConstant()) {
	  count += 1;
	  if (count > 1) break;
	  lastop = maybeop;
	}
      }
    }
    if (count == 1) {
      hiop = lastop;
      return 0;
    }
    if (count >1)
      return -1;		// Couldn't distinguish between multiple possibilities
  }
  return -2;
}

// Given a known double precision input, look for a double precision logical operation
// recovering the other double input and the double output
//
// Assume hi1, lo1 is a known double precision pair, we look for
// reshi = hi1 & hi2
// reslo = lo1 & lo2
bool LogicalForm::verify(Varnode *h,Varnode *l,PcodeOp *lop)

{
  loop = lop;
  lo1 = l;
  hi1 = h;
  int4 res = findHiMatch();

  if (res == 0) {		// We found a matching lo presis operation
    lo2 = loop->getIn(1-loop->getSlot(lo1));
    hi2 = hiop->getIn(1-hiop->getSlot(hi1));
    if ((lo2==lo1)||(lo2==hi1)||(hi2==hi1)||(hi2==lo1)) return false; // No manipulation of itself
    if (lo2 == hi2) return false;
    return true;
  }
  return false;
}

bool LogicalForm::applyRule(SplitVarnode &i,PcodeOp *lop,bool workishi,Funcdata &data)

{
  if (workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;

  if (!verify(in.getHi(),in.getLo(),lop))
    return false;

  outdoub.initPartial(in.getSize(),loop->getOut(),hiop->getOut());
  indoub.initPartial(in.getSize(),lo2,hi2);
  if (indoub.exceedsConstPrecision())
    return false;
  existop = SplitVarnode::prepareBinaryOp(outdoub,in,indoub);
  if (existop == (PcodeOp *)0)
    return false;

  SplitVarnode::createBinaryOp(data,outdoub,in,indoub,existop,loop->code());
  return true;
}

// Given a known double precis input, look for double precision compares of the form
//   a == b,  a != b
//
// We look for
//     hibool = hi1 == hi2
//     lobool = lo1 == lo2
// each of the bools induces a CBRANCH
//               if (hibool) blocksecond else blockfalse
// blocksecond:  if (lobool) blocktrue else blockfalse
bool Equal1Form::applyRule(SplitVarnode &i,PcodeOp *hop,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (!i.hasBothPieces()) return false;
  in1 = i;

  hiop = hop;
  hi1 = in1.getHi();
  lo1 = in1.getLo();
  hi1slot = hiop->getSlot(hi1);
  hi2 = hiop->getIn(1-hi1slot);
  notequalformhi = (hiop->code() == CPUI_INT_NOTEQUAL);

  list<PcodeOp *>::const_iterator iter,enditer;
  list<PcodeOp *>::const_iterator iter2,enditer2;
  list<PcodeOp *>::const_iterator iter3,enditer3;
  iter = lo1->beginDescend();
  enditer = lo1->endDescend();
  while(iter != enditer) {
    loop = *iter;
    ++iter;
    if (loop->code() == CPUI_INT_EQUAL)
      notequalformlo = false;
    else if (loop->code() == CPUI_INT_NOTEQUAL)
      notequalformlo = true;
    else
      continue;
    lo1slot = loop->getSlot(lo1);
    lo2 = loop->getIn(1-lo1slot);

    iter2 = hiop->getOut()->beginDescend();
    enditer2 = hiop->getOut()->endDescend();
    while(iter2 != enditer2) {
      hibool = *iter2;
      ++iter2;
      iter3 = loop->getOut()->beginDescend();
      enditer3 = loop->getOut()->endDescend();
      while(iter3 != enditer3) {
	lobool = *iter3;
	++iter3;

	in2.initPartial(in1.getSize(),lo2,hi2);
	if (in2.exceedsConstPrecision())
	  continue;
	
	if ((hibool->code() == CPUI_CBRANCH)&&(lobool->code()==CPUI_CBRANCH)) {
	  // Branching form of the equal operation
	  BlockBasic *hibooltrue,*hiboolfalse;
	  BlockBasic *lobooltrue,*loboolfalse;
	  SplitVarnode::getTrueFalse(hibool,notequalformhi,hibooltrue,hiboolfalse);
	  SplitVarnode::getTrueFalse(lobool,notequalformlo,lobooltrue,loboolfalse);
	  
	  if ((hibooltrue == lobool->getParent())&&	// hi is checked first then lo
	      (hiboolfalse == loboolfalse)&&
	      SplitVarnode::otherwiseEmpty(lobool)) {
	    if (SplitVarnode::prepareBoolOp(in1,in2,hibool)) {
	      setonlow = true;
	      SplitVarnode::createBoolOp(data,hibool,in1,in2,notequalformhi ? CPUI_INT_NOTEQUAL : CPUI_INT_EQUAL);
	  // We change lobool so that it always goes to the original TRUE block
	      data.opSetInput(lobool,data.newConstant(1,notequalformlo ? 0 : 1),1);
	      return true;
	    }
	  }
	  else if ((lobooltrue == hibool->getParent())&& // lo is checked first then hi
		   (hiboolfalse == loboolfalse)&&
		   SplitVarnode::otherwiseEmpty(hibool)) {
	    if (SplitVarnode::prepareBoolOp(in1,in2,lobool)) {
	      setonlow = false;
	      SplitVarnode::createBoolOp(data,lobool,in1,in2,notequalformlo ? CPUI_INT_NOTEQUAL : CPUI_INT_EQUAL);
	      // We change hibool so that it always goes to the original TRUE block
	      data.opSetInput(hibool,data.newConstant(1,notequalformhi ? 0 : 1),1);
	      return true;
	    }
	  }
	}
      }
    }
  }
  return false;
}

bool Equal2Form::replace(Funcdata &data)

{
  if (hi2->isConstant()&&lo2->isConstant()) {
    uintb val = hi2->getOffset();
    val <<= 8*lo1->getSize();
    val |= lo2->getOffset();
    param2.initPartial(in.getSize(),val);
    return SplitVarnode::prepareBoolOp(in,param2,boolAndOr);
  }
  if (hi2->isConstant()||lo2->isConstant()) {
    // Some kind of mixed form
    return false;
  }
  param2.initPartial(in.getSize(),lo2,hi2);
  return SplitVarnode::prepareBoolOp(in,param2,boolAndOr);
}

// Given a known double precis input, look for double precision compares of the form
//   a == b,  a != b
//
// We look for
//     res = (hi1 == hi2) && (lo1 == lo2) or
//     res = (hi1 != hi2) || (lo1 != lo2)
bool Equal2Form::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;
  hi1 = in.getHi();
  lo1 = in.getLo();
  OpCode eqCode = op->code();
  int4 hi1slot = op->getSlot(hi1);
  hi2 = op->getIn(1-hi1slot);
  Varnode *outvn = op->getOut();
  list<PcodeOp *>::const_iterator iter,enditer;
  iter = outvn->beginDescend();
  enditer = outvn->endDescend();
  while(iter != enditer) {
    boolAndOr = *iter;
    ++iter;
    if (eqCode == CPUI_INT_EQUAL && boolAndOr->code() != CPUI_BOOL_AND) continue;
    if (eqCode == CPUI_INT_NOTEQUAL && boolAndOr->code() != CPUI_BOOL_OR) continue;
    int4 slot = boolAndOr->getSlot(outvn);
    Varnode *othervn = boolAndOr->getIn(1-slot);
    if (!othervn->isWritten()) continue;
    PcodeOp *equalLo = othervn->getDef();
    if (equalLo->code() != eqCode) continue;
    if (equalLo->getIn(0) == lo1) {
      lo2 = equalLo->getIn(1);
    }
    else if (equalLo->getIn(1) == lo1) {
      lo2 = equalLo->getIn(0);
    }
    else {
      continue;
    }
    if (!replace(data)) continue;
    if (param2.exceedsConstPrecision()) continue;
    SplitVarnode::replaceBoolOp(data,boolAndOr,in,param2,eqCode);
    return true;
  }
  return false;
}

bool Equal3Form::verify(Varnode *h,Varnode *l,PcodeOp *aop)

{
  if (aop->code() != CPUI_INT_AND) return false;
  hi = h;
  lo = l;
  andop = aop;
  int4 hislot = andop->getSlot(hi);
  if (andop->getIn(1-hislot) != lo) return false;	// hi and lo must be ANDed together
  compareop = andop->getOut()->loneDescend();
  if (compareop == (PcodeOp *)0) return false;
  if ((compareop->code()!=CPUI_INT_EQUAL)&&(compareop->code()!=CPUI_INT_NOTEQUAL))
    return false;
  uintb allonesval = calc_mask(lo->getSize());
  smallc = compareop->getIn(1);
  if (!smallc->isConstant()) return false;
  if (smallc->getOffset() != allonesval) return false;
  return true;
}

// Given a known double precis input, look for double precision compares of the form
//   a == -1,  a != -1
//
// We look for
//     hi & lo == -1
bool Equal3Form::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;
  if (!verify(in.getHi(),in.getLo(),op))
    return false;

  SplitVarnode in2(in.getSize(),calc_mask(in.getSize()));	// Create the -1 value
  if (in2.exceedsConstPrecision())
    return false;
  if (!SplitVarnode::prepareBoolOp(in,in2,compareop)) return false;
  SplitVarnode::replaceBoolOp(data,compareop,in,in2,compareop->code());
  return true;
}

bool LessThreeWay::mapBlocksFromLow(BlockBasic *lobl)

{ // Assuming -lobl- is the block containing the low precision test of a double precision lessthan
  // Map out all the blocks if possible, otherwise return false
  lolessbl = lobl;
  if (lolessbl->sizeIn() != 1) return false;
  if (lolessbl->sizeOut() != 2) return false;
  hieqbl = (BlockBasic *)lolessbl->getIn(0);
  if (hieqbl->sizeIn() != 1) return false;
  if (hieqbl->sizeOut() != 2) return false;
  hilessbl = (BlockBasic *)hieqbl->getIn(0);
  if (hilessbl->sizeOut() != 2) return false;
  return true;
}

bool LessThreeWay::mapOpsFromBlocks(void)

{
  lolessbool = lolessbl->lastOp();
  if (lolessbool == (PcodeOp *)0) return false;
  if (lolessbool->code() != CPUI_CBRANCH) return false;
  hieqbool = hieqbl->lastOp();
  if (hieqbool == (PcodeOp *)0) return false;
  if (hieqbool->code() != CPUI_CBRANCH) return false;
  hilessbool = hilessbl->lastOp();
  if (hilessbool == (PcodeOp *)0) return false;
  if (hilessbool->code() != CPUI_CBRANCH) return false;

  Varnode *vn;

  hiflip = false;
  equalflip = false;
  loflip = false;
  midlessform = false;
  lolessiszerocomp = false;

  vn = hieqbool->getIn(1);
  if (!vn->isWritten()) return false;
  hiequal = vn->getDef();
  switch(hiequal->code()) {
  case CPUI_INT_EQUAL:
    midlessform = false;
    break;
  case CPUI_INT_NOTEQUAL:
    midlessform = false;
    break;
  case CPUI_INT_LESS:
    midlessequal = false;
    midsigncompare = false;
    midlessform = true;
    break;
  case CPUI_INT_LESSEQUAL:
    midlessequal = true;
    midsigncompare = false;
    midlessform = true;
    break;
  case CPUI_INT_SLESS:
    midlessequal = false;
    midsigncompare = true;
    midlessform = true;
    break;
  case CPUI_INT_SLESSEQUAL:
    midlessequal = true;
    midsigncompare = true;
    midlessform = true;
    break;
  default:
    return false;
  }

  vn = lolessbool->getIn(1);
  if (!vn->isWritten()) return false;
  loless = vn->getDef();
  switch(loless->code()) {	// Only unsigned forms
  case CPUI_INT_LESS:
    lolessequalform = false;
    break;
  case CPUI_INT_LESSEQUAL:
    lolessequalform = true;
    break;
  case CPUI_INT_EQUAL:
    if (!loless->getIn(1)->isConstant()) return false;
    if (loless->getIn(1)->getOffset()!=0) return false;
    lolessiszerocomp = true;
    lolessequalform = true;
    break;
  case CPUI_INT_NOTEQUAL:
    if (!loless->getIn(1)->isConstant()) return false;
    if (loless->getIn(1)->getOffset()!=0) return false;
    lolessiszerocomp = true;
    lolessequalform = false;
    break;
  default:
    return false;
  }

  vn = hilessbool->getIn(1);
  if (!vn->isWritten()) return false;
  hiless = vn->getDef();
  switch(hiless->code()){
  case CPUI_INT_LESS:
    hilessequalform = false;
    signcompare = false;
    break;
  case CPUI_INT_LESSEQUAL:
    hilessequalform = true;
    signcompare = false;
    break;
  case CPUI_INT_SLESS:
    hilessequalform = false;
    signcompare = true;
    break;
  case CPUI_INT_SLESSEQUAL:
    hilessequalform = true;
    signcompare = true;
    break;
  default:
    return false;
  }
  return true;
}

bool LessThreeWay::checkSignedness(void)

{
  if (midlessform) {
    if (midsigncompare != signcompare) return false;
  }
  return true;
}

bool LessThreeWay::normalizeHi(void)

{
  Varnode *tmpvn;
  vnhil1 = hiless->getIn(0);
  vnhil2 = hiless->getIn(1);
  if (vnhil1->isConstant()) {	// Start with constant on the right
    hiflip = !hiflip;
    hilessequalform = !hilessequalform;
    tmpvn = vnhil1;
    vnhil1 = vnhil2;
    vnhil2 = tmpvn;
  }
  hiconstform = false;
  if (vnhil2->isConstant()) {
    if (in.getSize() > sizeof(uintb)) return false;	// Must have enough precision for constant
    hiconstform = true;
    hival = vnhil2->getOffset();
    SplitVarnode::getTrueFalse(hilessbool,hiflip,hilesstrue,hilessfalse);
    int4 inc = 1;
    if (hilessfalse != hieqbl) {	// Make sure the hiless false branch goes to the hieq block
      hiflip = !hiflip;
      hilessequalform = !hilessequalform;
      tmpvn = vnhil1;
      vnhil1 = vnhil2;
      vnhil2 = tmpvn;
      inc = -1;
    }
    if (hilessequalform) {	// Make sure to normalize lessequal to less
      hival += inc;
      hival &= calc_mask(in.getSize());
      hilessequalform = false;
    }
    hival >>= in.getLo()->getSize() * 8;
  }
  else {
    if (hilessequalform) {	// Make sure the false branch contains the equal case
      hilessequalform = false;
      hiflip = !hiflip;
      tmpvn = vnhil1;
      vnhil1 = vnhil2;
      vnhil2 = tmpvn;
    }
  }
  return true;
}

bool LessThreeWay::normalizeMid(void)

{
  Varnode *tmpvn;
  vnhie1 = hiequal->getIn(0);
  vnhie2 = hiequal->getIn(1);
  if (vnhie1->isConstant()) {	// Make sure constant is on the right
    tmpvn = vnhie1;
    vnhie1 = vnhie2;
    vnhie2 = tmpvn;
    if (midlessform) {
      equalflip = !equalflip;
      midlessequal = !midlessequal;
    }
  }
  midconstform = false;
  if (vnhie2->isConstant()) {
    if (!hiconstform) return false;	// If mid is constant, both mid and hi must be constant
    midconstform = true;
    midval = vnhie2->getOffset();
    if (vnhie2->getSize() == in.getSize()) {
      // Convert to comparison on high part
      uintb lopart = midval & calc_mask(in.getLo()->getSize());
      midval >>= in.getLo()->getSize()*8;
      if (midlessform) {
	if (midlessequal) {
	  if (lopart!=calc_mask(in.getLo()->getSize())) return false;
	}
	else {
	  if (lopart != 0) return false;
	}
      }
      else
	return false;		// Compare is forcing restriction on lo part
    }
    if (midval != hival) {	// If the mid and hi don't match
      if (!midlessform) return false;
      midval += (midlessequal) ? 1 : -1; // We may just be one off
      midval &= calc_mask(in.getLo()->getSize());
      midlessequal = !midlessequal;
      if (midval != hival) return false; // Last chance
    }
  }
  if (midlessform) {		// Normalize to EQUAL
    
    if (!midlessequal) {
      equalflip = !equalflip;
    }
  }
  else {
    if (hiequal->code() == CPUI_INT_NOTEQUAL) {
      equalflip = !equalflip;
    }
  }
  return true;
}

bool LessThreeWay::normalizeLo(void)

{ // This is basically identical to normalizeHi
  Varnode *tmpvn;
  vnlo1 = loless->getIn(0);
  vnlo2 = loless->getIn(1);
  if (lolessiszerocomp) {
    loconstform = true;
    if (lolessequalform) {	// Treat as if we see vnlo1 <= 0
      loval = 1;
      lolessequalform = false;
    }
    else {			// Treat as if we see 0 < vnlo1
      loflip = !loflip;
      loval = 1;
    }
    return true;
  }
  if (vnlo1->isConstant()) {	// Make sure constant is on the right
    loflip = !loflip;
    lolessequalform = !lolessequalform;
    tmpvn = vnlo1;
    vnlo1 = vnlo2;
    vnlo2 = tmpvn;
  }
  loconstform = false;
  if (vnlo2->isConstant()) {	// Make sure normalize lessequal to less
    loconstform = true;
    loval = vnlo2->getOffset();
    if (lolessequalform) {
      loval += 1;
      loval &= calc_mask(vnlo2->getSize());
      lolessequalform = false;
    }
  }
  else {
    if (lolessequalform) {
      lolessequalform = false;
      loflip = !loflip;
      tmpvn = vnlo1;
      vnlo1 = vnlo2;
      vnlo2 = tmpvn;
    }
  }
  return true;
}

bool LessThreeWay::checkBlockForm(void)

{
  SplitVarnode::getTrueFalse(hilessbool,hiflip,hilesstrue,hilessfalse);
  SplitVarnode::getTrueFalse(lolessbool,loflip,lolesstrue,lolessfalse);
  SplitVarnode::getTrueFalse(hieqbool,equalflip,hieqtrue,hieqfalse);
  if ((hilesstrue == lolesstrue)&&
      (hieqfalse == lolessfalse)&&
      (hilessfalse == hieqbl)&&
      (hieqtrue == lolessbl)) {
    if (SplitVarnode::otherwiseEmpty(hieqbool)&&SplitVarnode::otherwiseEmpty(lolessbool))
      return true;
  }
  //  else if ((hilessfalse == lolessfalse)&&
  //	   (hieqfalse == lolesstrue)&&
  //	   (hilesstrue == hieqbl)&&
  //	   (hieqtrue == lolessbl)) {
  //    if (SplitVarnode::otherwiseEmpty(hieqbool)&&SplitVarnode::otherwiseEmpty(lolessbool))
  //      return true;
  //  }
  return false;
}

bool LessThreeWay::checkOpForm(void)

{
  lo = in.getLo();
  hi = in.getHi();

  if (midconstform) {
    if (!hiconstform) return false;
    if (vnhie2->getSize() == in.getSize()) {
      if ((vnhie1!=vnhil1)&&(vnhie1!=vnhil2)) return false;
    }
    else {
      if (vnhie1!=in.getHi()) return false;
    }
    // normalizeMid checks that midval == hival
  }
  else {
    // hi and hi2 must appear as inputs in both -hiless- and -hiequal-
    if ((vnhil1!=vnhie1)&&(vnhil1!=vnhie2)) return false;
    if ((vnhil2!=vnhie1)&&(vnhil2!=vnhie2)) return false;
  }
  if ((hi!=(Varnode *)0)&&(hi == vnhil1)) {
    if (hiconstform) return false;
    hislot = 0;
    hi2 = vnhil2;
    if (vnlo1 != lo) { // Pieces must be on the same side
      Varnode *tmpvn = vnlo1;
      vnlo1 = vnlo2;
      vnlo2 = tmpvn;
      if (vnlo1 != lo) return false;
      loflip = !loflip;
      lolessequalform = !lolessequalform;
    }
    lo2 = vnlo2;
  }
  else if ((hi!=(Varnode *)0)&&(hi == vnhil2)) {
    if (hiconstform) return false;
    hislot = 1;
    hi2 = vnhil1;
    if (vnlo2 != lo) {
      Varnode *tmpvn = vnlo1;
      vnlo1 = vnlo2;
      vnlo2 = tmpvn;
      if (vnlo2 != lo) return false;
      loflip = !loflip;
      lolessequalform = !lolessequalform;
    }
    lo2 = vnlo1;
  }
  else if (in.getWhole() == vnhil1) {
    if (!hiconstform) return false;
    if (!loconstform) return false;
    if (vnlo1 != lo) return false;
    hislot = 0;
  }
  else if (in.getWhole() == vnhil2) { // Whole constant appears on the left
    if (!hiconstform) return false;
    if (!loconstform) return false;
    if (vnlo2 != lo) {
      loflip = !loflip;
      loval -= 1;
      loval &= calc_mask(lo->getSize());
      if (vnlo1 != lo) return false;
    }
    hislot = 1;
  }
  else
    return false;

  return true;
}

void LessThreeWay::setOpCode(void)

{ // Decide on the opcode of the final double precision compare
  if (lolessequalform != hiflip)
    finalopc = signcompare ? CPUI_INT_SLESSEQUAL : CPUI_INT_LESSEQUAL;
  else
    finalopc = signcompare ? CPUI_INT_SLESS : CPUI_INT_LESS;
  if (hiflip) {
    hislot = 1-hislot;
    hiflip = false;
  }
}

bool LessThreeWay::setBoolOp(void)

{ // Make changes to the threeway branch so that it becomes a single double precision branch
  if (hislot==0) {
    if (SplitVarnode::prepareBoolOp(in,in2,hilessbool))
      return true;
  }
  else {
    if (SplitVarnode::prepareBoolOp(in2,in,hilessbool))
      return true;
  }
  return false;
}

bool LessThreeWay::mapFromLow(PcodeOp *op)

{ // Given the less than comparison for the lo piece and an input varnode explicitly marked as isPrecisLo
  // try to map out the threeway lessthan form
  PcodeOp *loop = op->getOut()->loneDescend();
  if (loop == (PcodeOp *)0) return false;
  if (!mapBlocksFromLow(loop->getParent())) return false;
  if (!mapOpsFromBlocks()) return false;
  if (!checkSignedness()) return false;
  if (!normalizeHi()) return false;
  if (!normalizeMid()) return false;
  if (!normalizeLo()) return false;
  if (!checkOpForm()) return false;
  if (!checkBlockForm()) return false;
  return true;
}

bool LessThreeWay::testReplace(void)

{
  setOpCode();
  if (hiconstform) {
    in2.initPartial(in.getSize(),(hival<<(8*in.getLo()->getSize()))|loval);
    if (!setBoolOp()) return false;
  }
  else {
    in2.initPartial(in.getSize(), lo2,hi2);
    if (!setBoolOp()) return false;
  }
  return true;
}

// Given a known double precis input, look for double precision less than forms, i.e.
//    a < b,   a s< b,  a <= b,   a s<= b
//
// In this form we look for three separate comparison ops
//     hiless  = hi1 LESS hi2                   where LESS is in { <, s<, <=, s<= }
//     hiequal = hi1 == hi2
//     loless  = lo1 < lo2  OR  lo1 <= lo2      where the comparison is unsigned
//
// This boolean values are either combined in the following formula:
//     resbool = hiless || (hiequal && loless)
// OR each of the three initial comparison induces a CBRANCH
//                  if (hiless)  blocktrue  else  blocksecond
//     blocksecond: if (hiequal) blockthird else  blockfalse
//     blockthird:  if (loless) blocktrue else blockfalse
bool LessThreeWay::applyRule(SplitVarnode &i,PcodeOp *loop,bool workishi,Funcdata &data)

{
  if (workishi) return false;
  if (i.getLo() == (Varnode *)0) return false; // Doesn't necessarily need the hi
  in = i;
  if (!mapFromLow(loop)) return false;
  bool res = testReplace();
  if (res) {
    if (in2.exceedsConstPrecision())
      return false;
    if (hislot==0)
      SplitVarnode::createBoolOp(data,hilessbool,in,in2,finalopc);
    else
      SplitVarnode::createBoolOp(data,hilessbool,in2,in,finalopc);
    // We change hieqbool so that it always goes to the original FALSE block
    data.opSetInput(hieqbool,data.newConstant(1,equalflip ? 1 : 0),1);
    // The lolessbool block now becomes unreachable and is eventually removed
  }
  return res;
}

// Sometimes double precision compares only involve the high portion of the value
// The canonical example being determining whether val > 0, where we only have to
// calculate (hi > 0).  This rule takes
//    hi COMPARE #const
// and transforms it to 
//    whole COMPARE #constextend
// where #constextend is built from #const by postpending either all 0 bits or 1 bits
bool LessConstForm::applyRule(SplitVarnode &i,PcodeOp *op,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (i.getHi()==(Varnode *)0) return false; // We don't necessarily need the lo part
  in = i;
  vn = in.getHi();
  inslot = op->getSlot(vn);
  cvn = op->getIn(1-inslot);
  int4 losize = in.getSize() - vn->getSize();

  if (!cvn->isConstant()) return false;

  signcompare = ((op->code()==CPUI_INT_SLESSEQUAL)||(op->code()==CPUI_INT_SLESS));
  hilessequalform = ((op->code()==CPUI_INT_SLESSEQUAL)||(op->code()==CPUI_INT_LESSEQUAL));

  uintb val = cvn->getOffset() << 8*losize;
  if (hilessequalform != (inslot==1))
    val |= calc_mask(losize);

  // This rule can apply and mess up less,equal rules, so we only apply it if it directly affects a branch
  PcodeOp *desc = op->getOut()->loneDescend();
  if (desc == (PcodeOp *)0) return false;
  if (desc->code() != CPUI_CBRANCH) return false;

  constin.initPartial(in.getSize(),val);
  if (constin.exceedsConstPrecision())
    return false;

  if (inslot==0) {
    if (SplitVarnode::prepareBoolOp(in,constin,op)) {
      SplitVarnode::replaceBoolOp(data,op,in,constin,op->code());
      return true;
    }
  }
  else {
    if (SplitVarnode::prepareBoolOp(constin,in,op)) {
      SplitVarnode::replaceBoolOp(data,op,constin,in,op->code());
      return true;
    }
  }
  
  return false;
}

bool ShiftForm::mapLeft(void)

{ // Assume reshi, reslo are filled in, fill in other ops and varnodes
  if (!reslo->isWritten()) return false;
  if (!reshi->isWritten()) return false;
  loshift = reslo->getDef();
  opc = loshift->code();
  if (opc != CPUI_INT_LEFT) return false;
  orop = reshi->getDef();
  if ((orop->code() != CPUI_INT_OR)&&(orop->code() != CPUI_INT_XOR)&&(orop->code() != CPUI_INT_ADD))
    return false;
  midlo = orop->getIn(0);
  midhi = orop->getIn(1);
  if (!midlo->isWritten()) return false;
  if (!midhi->isWritten()) return false;
  if (midhi->getDef()->code() != CPUI_INT_LEFT) {
    Varnode *tmpvn = midhi;
    midhi = midlo;
    midlo = tmpvn;
  }
  midshift = midlo->getDef();
  if (midshift->code() != CPUI_INT_RIGHT) return false;	// Must be unsigned RIGHT
  hishift = midhi->getDef();
  if (hishift->code() != CPUI_INT_LEFT) return false;

  if (lo != loshift->getIn(0)) return false;
  if (hi != hishift->getIn(0)) return false;
  if (lo != midshift->getIn(0)) return false;
  salo = loshift->getIn(1);
  sahi = hishift->getIn(1);
  samid = midshift->getIn(1);
  return true;
}

bool ShiftForm::mapRight(void)

{ // Assume reshi, reslo are filled in, fill in other ops and varnodes
  if (!reslo->isWritten()) return false;
  if (!reshi->isWritten()) return false;
  hishift = reshi->getDef();
  opc = hishift->code();
  if ((opc != CPUI_INT_RIGHT)&&(opc != CPUI_INT_SRIGHT)) return false;
  orop = reslo->getDef();
  if ((orop->code() != CPUI_INT_OR)&&(orop->code() != CPUI_INT_XOR)&&(orop->code() != CPUI_INT_ADD))
    return false;
  midlo = orop->getIn(0);
  midhi = orop->getIn(1);
  if (!midlo->isWritten()) return false;
  if (!midhi->isWritten()) return false;
  if (midlo->getDef()->code() != CPUI_INT_RIGHT) { // Must be unsigned RIGHT
    Varnode *tmpvn = midhi;
    midhi = midlo;
    midlo = tmpvn;
  }
  midshift = midhi->getDef();
  if (midshift->code() != CPUI_INT_LEFT) return false;
  loshift = midlo->getDef();
  if (loshift->code() != CPUI_INT_RIGHT) return false; // Must be unsigned RIGHT

  if (lo != loshift->getIn(0)) return false;
  if (hi != hishift->getIn(0)) return false;
  if (hi != midshift->getIn(0)) return false;
  salo = loshift->getIn(1);
  sahi = hishift->getIn(1);
  samid = midshift->getIn(1);
  return true;
}

bool ShiftForm::verifyShiftAmount(void)

{ // Make sure all the shift amount varnodes are consistent
  if (!salo->isConstant()) return false;
  if (!samid->isConstant()) return false;
  if (!sahi->isConstant()) return false;
  uintb val = salo->getOffset();
  if (val != sahi->getOffset()) return false;
  if (val >= 8*lo->getSize()) return false; // If shift amount is so big, we would not use this form
  val = 8*lo->getSize() - val;
  if (samid->getOffset() != val) return false;
  return true;
}

bool ShiftForm::verifyLeft(Varnode *h,Varnode *l,PcodeOp *loop)

{
  hi = h;
  lo = l;

  loshift = loop;
  reslo = loshift->getOut();
  
  list<PcodeOp *>::const_iterator iter,enditer;
  iter = hi->beginDescend();
  enditer = hi->endDescend();
  while(iter != enditer) {
    hishift = *iter;
    ++iter;
    if (hishift->code() != CPUI_INT_LEFT) continue;
    Varnode *outvn = hishift->getOut();
    list<PcodeOp *>::const_iterator iter2,enditer2;
    iter2 = outvn->beginDescend();
    enditer2 = outvn->endDescend();
    while(iter2 != enditer2) {
      midshift = *iter2;
      ++iter2;
      Varnode *tmpvn = midshift->getOut();
      if (tmpvn == (Varnode *)0) continue;
      reshi = tmpvn;
      if (!mapLeft()) continue;
      if (!verifyShiftAmount()) continue;
      return true;
    }
  }
  return false;
}

bool ShiftForm::verifyRight(Varnode *h,Varnode *l,PcodeOp *hiop)

{
  hi = h;
  lo = l;
  hishift = hiop;
  reshi = hiop->getOut();
  
  list<PcodeOp *>::const_iterator iter,enditer;
  iter = lo->beginDescend();
  enditer = lo->endDescend();
  while(iter != enditer) {
    loshift = *iter;
    ++iter;
    if (loshift->code() != CPUI_INT_RIGHT) continue;
    Varnode *outvn = loshift->getOut();
    list<PcodeOp *>::const_iterator iter2,enditer2;
    iter2 = outvn->beginDescend();
    enditer2 = outvn->endDescend();
    while(iter2 != enditer2) {
      midshift = *iter2;
      ++iter2;
      Varnode *tmpvn = midshift->getOut();
      if (tmpvn == (Varnode *)0) continue;
      reslo = tmpvn;
      if (!mapRight()) continue;
      if (!verifyShiftAmount()) continue;
      return true;
    }
  }
  return false;
}

bool ShiftForm::applyRuleLeft(SplitVarnode &i,PcodeOp *loop,bool workishi,Funcdata &data)

{
  if (workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;

  if (!verifyLeft(in.getHi(),in.getLo(),loop))
    return false;

  out.initPartial(in.getSize(),reslo,reshi);
  existop = SplitVarnode::prepareShiftOp(out,in);
  if (existop == (PcodeOp *)0)
    return false;
  SplitVarnode::createShiftOp(data,out,in,salo,existop,opc);
  return true;
}

bool ShiftForm::applyRuleRight(SplitVarnode &i,PcodeOp *hiop,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;

  if (!verifyRight(in.getHi(),in.getLo(),hiop))
    return false;

  out.initPartial(in.getSize(),reslo,reshi);
  existop = SplitVarnode::prepareShiftOp(out,in);
  if (existop == (PcodeOp *)0)
    return false;
  SplitVarnode::createShiftOp(data,out,in,salo,existop,opc);
  return true;
}

bool MultForm::mapResHiSmallConst(Varnode *rhi)

{ // find reshi=hi1*lo2 + (tmp>>32)
  reshi = rhi;
  if (!reshi->isWritten()) return false;
  add1 = reshi->getDef();
  if (add1->code() != CPUI_INT_ADD) return false;
  Varnode *ad1,*ad2;
  ad1 = add1->getIn(0);
  ad2 = add1->getIn(1);
  if (!ad1->isWritten()) return false;
  if (!ad2->isWritten()) return false;
  multhi1 = ad1->getDef();
  if (multhi1->code() != CPUI_INT_MULT) {
    subhi = multhi1;
    multhi1 = ad2->getDef();
  }
  else
    subhi = ad2->getDef();
  if (multhi1->code() != CPUI_INT_MULT) return false;
  if (subhi->code() != CPUI_SUBPIECE) return false;
  midtmp = subhi->getIn(0);
  if (!midtmp->isWritten()) return false;
  multlo = midtmp->getDef();
  if (multlo->code() != CPUI_INT_MULT) return false;
  lo1zext = multlo->getIn(0);
  lo2zext = multlo->getIn(1);
  return true;
}
  
bool MultForm::mapResHi(Varnode *rhi)

{ // Find reshi=hi1*lo2 + hi2*lo1 + (tmp>>32)
  reshi = rhi;
  if (!reshi->isWritten()) return false;
  add1 = reshi->getDef();
  if (add1->code() != CPUI_INT_ADD) return false;
  Varnode *ad1,*ad2,*ad3;
  ad1 = add1->getIn(0);
  ad2 = add1->getIn(1);
  if (!ad1->isWritten()) return false;
  if (!ad2->isWritten()) return false;
  add2 = ad1->getDef();
  if (add2->code() == CPUI_INT_ADD) {
    ad1 = add2->getIn(0);
    ad3 = add2->getIn(1);
  }
  else {
    add2 = ad2->getDef();
    if (add2->code() != CPUI_INT_ADD) return false;
    ad2 = add2->getIn(0);
    ad3 = add2->getIn(1);
  }
  if (!ad1->isWritten()) return false;
  if (!ad2->isWritten()) return false;
  if (!ad3->isWritten()) return false;
  subhi = ad1->getDef();
  if (subhi->code() == CPUI_SUBPIECE) {
    multhi1 = ad2->getDef();
    multhi2 = ad3->getDef();
  }
  else {
    subhi = ad2->getDef();
    if (subhi->code() == CPUI_SUBPIECE) {
      multhi1 = ad1->getDef();
      multhi2 = ad3->getDef();
    }
    else {
      subhi = ad3->getDef();
      if (subhi->code() == CPUI_SUBPIECE) {
	multhi1 = ad1->getDef();
	multhi2 = ad2->getDef();
      }
      else
	return false;
    }
  }
  if (multhi1->code() != CPUI_INT_MULT) return false;
  if (multhi2->code() != CPUI_INT_MULT) return false;

  midtmp = subhi->getIn(0);
  if (!midtmp->isWritten()) return false;
  multlo = midtmp->getDef();
  if (multlo->code() != CPUI_INT_MULT) return false;
  lo1zext = multlo->getIn(0);
  lo2zext = multlo->getIn(1);
  return true;
}

bool MultForm::findLoFromInSmallConst(void)

{ // Assuming we have -multhi1-, -lo1-, and -hi1- in hand, try to label -lo2-
  Varnode *vn1 = multhi1->getIn(0);
  Varnode *vn2 = multhi1->getIn(1);
  if (vn1 == hi1)
    lo2 = vn2;
  else if (vn2 == hi1)
    lo2 = vn1;
  else
    return false;
  if (!lo2->isConstant()) return false;
  hi2 = (Varnode *)0;		// hi2 is an implied zero in this case
  return true;
}

bool MultForm::findLoFromIn(void)

{ // Assuming we have -multhi1-, -multhi2-, -lo1-, and -hi1- in hand, try to label lo2/hi2 pair
  Varnode *vn1 = multhi1->getIn(0);
  Varnode *vn2 = multhi1->getIn(1);
  if ((vn1 != lo1)&&(vn2!=lo1)) { // Try to normalize so multhi1 contains lo1
    PcodeOp *tmpop = multhi1;
    multhi1 = multhi2;
    multhi2 = tmpop;
    vn1 = multhi1->getIn(0);
    vn2 = multhi1->getIn(1);
  }
  if (vn1 == lo1)
    hi2 = vn2;
  else if (vn2 == lo1)
    hi2 = vn1;
  else
    return false;
  vn1 = multhi2->getIn(0);	// multhi2 should contain hi1 and lo2
  vn2 = multhi2->getIn(1);
  if (vn1 == hi1)
    lo2 = vn2;
  else if (vn2 == hi1)
    lo2 = vn1;
  else
    return false;

  return true;
}

bool MultForm::zextOf(Varnode *big,Varnode *small)

{ // Verify that big is (some form of) a zero extension of small
  PcodeOp *op;
  if (small->isConstant()) {
    if (!big->isConstant()) return false;
    if (big->getOffset() == small->getOffset()) return true;
    return false;
  }
  if (!big->isWritten()) return false;
  op = big->getDef();
  if (op->code() == CPUI_INT_ZEXT)
    return (op->getIn(0) == small);
  if (op->code() == CPUI_INT_AND) {
    if (!op->getIn(1)->isConstant()) return false;
    if (op->getIn(1)->getOffset() != calc_mask(small->getSize())) return false;
    Varnode *whole = op->getIn(0);
    if (!small->isWritten()) return false;
    PcodeOp *sub = small->getDef();
    if (sub->code() != CPUI_SUBPIECE) return false;
    return (sub->getIn(0) == whole);
  }
  return false;
}

bool MultForm::verifyLo(void)

{ // Given we have labelled lo1/hi1 lo2/hi2, make sure midtmp is formed properly
  // This also works for the small constant model  lo1/hi1 and lo2 const.
  if (subhi->getIn(1)->getOffset() != lo1->getSize()) return false;
  if (zextOf(lo1zext,lo1)) {
    if (zextOf(lo2zext,lo2))
      return true;
  }
  else if (zextOf(lo1zext,lo2)) {
    if (zextOf(lo2zext,lo1))
      return true;
  }
  return false;
}

bool MultForm::findResLo(void)

{ // Assuming we found -midtmp-, find potential reslo
  list<PcodeOp *>::const_iterator iter,enditer;
  iter = midtmp->beginDescend();
  enditer = midtmp->endDescend();
  while(iter != enditer) {
    PcodeOp *op = *iter;
    ++iter;
    if (op->code() != CPUI_SUBPIECE) continue;
    if (op->getIn(1)->getOffset() != 0) continue; // Must grab low bytes
    reslo = op->getOut();
    if (reslo->getSize() != lo1->getSize()) continue;
    return true;
  }
  // If we reach here, it may be that separate multiplies of lo1*lo2 were used for reshi and reslo
  iter = lo1->beginDescend();
  enditer = lo1->endDescend();
  while(iter != enditer) {
    PcodeOp *op = *iter;
    ++iter;
    if (op->code() != CPUI_INT_MULT) continue;
    Varnode *vn1 = op->getIn(0);
    Varnode *vn2 = op->getIn(1);
    if (lo2->isConstant()) {
      if ((!vn1->isConstant() || (vn1->getOffset() != lo2->getOffset())) &&
	  (!vn2->isConstant() || (vn2->getOffset() != lo2->getOffset())))
	continue;
    }
    else 
      if ((op->getIn(0)!=lo2)&&(op->getIn(1)!=lo2)) continue;
    reslo = op->getOut();
    return true;
  }
  return false;
}

bool MultForm::mapFromInSmallConst(Varnode *rhi)

{
  if (!mapResHiSmallConst(rhi)) return false;
  if (!findLoFromInSmallConst()) return false;
  if (!verifyLo()) return false;
  if (!findResLo()) return false;
  return true;
}

bool MultForm::mapFromIn(Varnode *rhi)

{ // Try to do full mapping from -in- given a putative reshi
  if (!mapResHi(rhi)) return false;
  if (!findLoFromIn()) return false;
  if (!verifyLo()) return false;
  if (!findResLo()) return false;
  return true;
}

bool MultForm::replace(Funcdata &data)

{ // We have matched a double precision multiply, now transform to logical variables
  outdoub.initPartial(in.getSize(),reslo,reshi);
  in2.initPartial(in.getSize(),lo2,hi2);
  if (in2.exceedsConstPrecision())
    return false;
  existop = SplitVarnode::prepareBinaryOp(outdoub,in,in2);
  if (existop == (PcodeOp *)0)
    return false;
  SplitVarnode::createBinaryOp(data,outdoub,in,in2,existop,CPUI_INT_MULT);
  return true;
}

bool MultForm::verify(Varnode *h,Varnode *l,PcodeOp *hop)

{
  hi1 = h;
  lo1 = l;
  list<PcodeOp *>::const_iterator iter,enditer;
  iter = hop->getOut()->beginDescend();
  enditer = hop->getOut()->endDescend();
  while(iter != enditer) {
    add1 = *iter;
    ++iter;
    if (add1->code() != CPUI_INT_ADD) continue;
    list<PcodeOp *>::const_iterator iter2,enditer2;
    iter2 = add1->getOut()->beginDescend();
    enditer2 = add1->getOut()->endDescend();
    while(iter2 != enditer2) {
      add2 = *iter2;
      ++iter2;
      if (add2->code() != CPUI_INT_ADD) continue;
      if (mapFromIn(add2->getOut()))
	return true;
    }
    if (mapFromIn(add1->getOut()))
      return true;
    if (mapFromInSmallConst(add1->getOut()))
      return true;
  }
  return false;
}

bool MultForm::applyRule(SplitVarnode &i,PcodeOp *hop,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;

  if (!verify(in.getHi(),in.getLo(),hop))
    return false;

  if (replace(data)) return true;
  return false;
}

// Given a known double precis coming together with two other pieces (via phi-nodes)
// Create a double precision phi-node
bool PhiForm::verify(Varnode *h,Varnode *l,PcodeOp *hphi)

{
  hibase = h;
  lobase = l;
  hiphi = hphi;

  inslot = hiphi->getSlot(hibase);

  if (hiphi->getOut()->hasNoDescend()) return false;
  blbase = hiphi->getParent();

  list<PcodeOp *>::const_iterator iter,enditer;
  iter = lobase->beginDescend();
  enditer = lobase->endDescend();
  while(iter != enditer) {
    lophi = *iter;
    ++iter;
    if (lophi->code() != CPUI_MULTIEQUAL) continue;
    if (lophi->getParent() != blbase) continue;
    if (lophi->getIn(inslot) != lobase) continue;
    return true;
  }
  return false;
}

bool PhiForm::applyRule(SplitVarnode &i,PcodeOp *hphi,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;

  if (!verify(in.getHi(),in.getLo(),hphi))
    return false;

  int4 numin = hiphi->numInput();
  vector<SplitVarnode> inlist;
  for(int4 j=0;j<numin;++j) {
    Varnode *vhi = hiphi->getIn(j);
    Varnode *vlo = lophi->getIn(j);
    inlist.push_back(SplitVarnode(vlo,vhi));
  }
  outvn.initPartial(in.getSize(),lophi->getOut(),hiphi->getOut());
  existop = SplitVarnode::preparePhiOp(outvn,inlist);
  if (existop != (PcodeOp *)0) {
    SplitVarnode::createPhiOp(data,outvn,inlist,existop);
    return true;
  }
  return false;
}

bool IndirectForm::verify(Varnode *h,Varnode *l,PcodeOp *ind)

{  // Verify the basic double precision indirect form and fill out the pieces
  hi = h;
  lo = l;
  indhi = ind;
  if (indhi->getIn(1)->getSpace()->getType()!=IPTR_IOP) return false;
  affector = PcodeOp::getOpFromConst(indhi->getIn(1)->getAddr());
  if (affector->isDead()) return false;
  reshi = indhi->getOut();
  if (reshi->getSpace()->getType() == IPTR_INTERNAL) return false;		// Indirect must not be through a temporary

  list<PcodeOp *>::const_iterator iter,enditer;
  iter = lo->beginDescend();
  enditer = lo->endDescend();
  while(iter != enditer) {
    indlo = *iter;
    ++iter;
    if (indlo->code() != CPUI_INDIRECT) continue;
    if (indlo->getIn(1)->getSpace()->getType()!=IPTR_IOP) continue;
    if (affector != PcodeOp::getOpFromConst(indlo->getIn(1)->getAddr())) continue;	// hi and lo must be affected by same op
    reslo = indlo->getOut();
    if (reslo->getSpace()->getType() == IPTR_INTERNAL) return false;		// Indirect must not be through a temporary
    if (reslo->isAddrTied() || reshi->isAddrTied()) {
      Address addr;
      // If one piece is address tied, the other must be as well, and they must fit together as contiguous whole
      if (!SplitVarnode::isAddrTiedContiguous(reslo, reshi, addr))
	return false;
    }
    return true;
  }
  return false;
}

bool IndirectForm::applyRule(SplitVarnode &i,PcodeOp *ind,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;
  if (!verify(in.getHi(),in.getLo(),ind))
    return false;

  outvn.initPartial(in.getSize(),reslo,reshi);

  if (!SplitVarnode::prepareIndirectOp(in,affector))
    return false;
  SplitVarnode::replaceIndirectOp(data,outvn,in,affector);
  return true;
}

/// Starting with the input pieces, identify the matching COPYs and verify that they act as a single
/// address forced COPY with no descendants.
/// \param h is the most significant input piece
/// \param l is the least significant input piece
/// \param w is the preexisting logical whole
/// \param cpy is the COPY of the most significant piece
bool CopyForceForm::verify(Varnode *h,Varnode *l,Varnode *w,PcodeOp *cpy)

{
  if (w == (Varnode *)0)
    return false;
  copyhi = cpy;
  if (copyhi->getIn(0) != h) return false;
  reshi = copyhi->getOut();
  if (!reshi->isAddrForce() || !reshi->hasNoDescend())
    return false;
  list<PcodeOp *>::const_iterator iter,enditer;
  iter = l->beginDescend();
  enditer = l->endDescend();
  while(iter != enditer) {
    copylo = *iter;
    ++iter;
    if (copylo->code() != CPUI_COPY || copylo->getParent() != copyhi->getParent())
      continue;
    reslo = copylo->getOut();
    if (!reslo->isAddrForce() || !reslo->hasNoDescend())
      continue;
    if (!SplitVarnode::isAddrTiedContiguous(reslo, reshi, addrOut))	// Output MUST be contiguous addresses
      continue;
    if (copyhi->isReturnCopy()) {	// Special form has additional requirements
      if (h->loneDescend() == (PcodeOp *)0)
	continue;
      if (l->loneDescend() == (PcodeOp *)0)
	continue;
      if (w->getAddr() != addrOut) {		// Input whole MUST also be the same address
	// Unless there are addition COPYs from the same basic block
	if (!h->isWritten() || !l->isWritten())
	  continue;
	PcodeOp *otherLo = l->getDef();
	PcodeOp *otherHi = h->getDef();
	if (otherLo->code() != CPUI_COPY || otherHi->code() != CPUI_COPY)
	  continue;
	if (otherLo->getParent() != otherHi->getParent())
	  continue;
      }
    }
    return true;
  }
  return false;
}

/// \param i is the putative input to the COPYs
/// \param cpy is a putative COPY
/// \param workishi is \b true if the COPY is of the most significant piece
/// \param data is the function
bool CopyForceForm::applyRule(SplitVarnode &i,PcodeOp *cpy,bool workishi,Funcdata &data)

{
  if (!workishi) return false;
  if (!i.hasBothPieces()) return false;
  in = i;
  if (!verify(in.getHi(),in.getLo(),in.getWhole(),cpy))
    return false;
  SplitVarnode::replaceCopyForce(data, addrOut, in, copylo, copyhi);
  return true;
}

void RuleDoubleIn::reset(Funcdata &data)

{
  data.setDoublePrecisRecovery(true); // Mark that we are doing double precision recovery
}

void RuleDoubleIn::getOpList(vector<uint4> &oplist) const

{
  oplist.push_back(CPUI_SUBPIECE);
}

/// \brief Determine if the given Varnode from a SUBPIECE should be marked as a double precision piece
///
/// If the given Varnode looks like the most significant piece, there is another SUBPIECE that looks
/// like the least significant piece, and the whole is from an operation that produces a logical whole,
/// then mark the Varnode (and its companion) as double precision pieces and return 1.
/// \param vn is the given Varnode
/// \param subpieceOp is the SUBPIECE PcodeOp producing the Varnode
/// \return 1 if the pieces are marked, 0 otherwise
int4 RuleDoubleIn::attemptMarking(Varnode *vn,PcodeOp *subpieceOp)

{
  Varnode *whole = subpieceOp->getIn(0);
  if (whole->isTypeLock()) {
    if (!whole->getType()->isPrimitiveWhole())
      return 0;		// Don't mark for double precision if not a primitive type
  }
  int4 offset = (int4)subpieceOp->getIn(1)->getOffset();
  if (offset != vn->getSize()) return 0;
  if (offset * 2 != whole->getSize()) return 0;		// Truncate exactly half
  if (whole->isInput()) {
    if (!whole->isTypeLock()) return 0;
  }
  else if (!whole->isWritten()) {
    return 0;
  }
  else {
    // Categorize opcodes as "producing a logical whole"
    // Its hard to tell if a logical op is really being used to act on the "logical whole"
    TypeOp *typeop = whole->getDef()->getOpcode();
    if (!typeop->isArithmeticOp() && !typeop->isFloatingPointOp())
      return 0;
  }
  Varnode *vnLo = (Varnode *)0;
  list<PcodeOp *>::const_iterator iter;
  for(iter=whole->beginDescend();iter!=whole->endDescend();++iter) {
    PcodeOp *op = *iter;
    if (op->code() != CPUI_SUBPIECE) continue;
    if (op->getIn(1)->getOffset() != 0) continue;
    if (op->getOut()->getSize() == vn->getSize()) {
      vnLo = op->getOut();
      break;
    }
  }
  if (vnLo == (Varnode *)0) return 0;
  vnLo->setPrecisLo();
  vn->setPrecisHi();
  return 1;
}

int4 RuleDoubleIn::applyOp(PcodeOp *op,Funcdata &data)

{
  Varnode *outvn = op->getOut();
  if (!outvn->isPrecisLo()) {
    if (outvn->isPrecisHi()) return 0;
    return attemptMarking(outvn, op);
  }
  if (data.hasUnreachableBlocks()) return 0;

  vector<SplitVarnode> splitvec;
  SplitVarnode::wholeList(op->getIn(0),splitvec);
  if (splitvec.empty()) return 0;
  for(int4 i=0;i<splitvec.size();++i) {
    SplitVarnode &in(splitvec[i]);
    int4 res = SplitVarnode::applyRuleIn(in,data);
    if (res != 0)
      return res;
  }
  return 0;
}

void RuleDoubleOut::getOpList(vector<uint4> &oplist) const

{
  oplist.push_back(CPUI_PIECE);
}

/// \brief Determine if the given inputs to a PIECE should marked as double precision pieces
///
/// If the concatenation of the pieces is used as a logical whole by other ops, the two pieces
/// are marked and 1 is returned.
/// \param vnhi is the most significant input to the PIECE
/// \param vnlo is the least significant input
/// \param pieceOp is the op reading the pieces
/// \return 1 if the pieces are marked, 0 otherwise
int4 RuleDoubleOut::attemptMarking(Varnode *vnhi,Varnode *vnlo,PcodeOp *pieceOp)

{
  Varnode *whole = pieceOp->getOut();
  if (whole->isTypeLock()) {
    if (!whole->getType()->isPrimitiveWhole())
      return 0;		// Don't mark for double precision if not a primitive type
  }
  if (vnhi->getSize() != vnlo->getSize())
    return 0;

  SymbolEntry *entryhi = vnhi->getSymbolEntry();
  SymbolEntry *entrylo = vnlo->getSymbolEntry();
  if (entryhi != (SymbolEntry *)0 || entrylo != (SymbolEntry *)0) {
    if (entryhi == (SymbolEntry *)0 || entrylo == (SymbolEntry *)0)
      return 0;		// One has a symbol, one doesn't
    if (entryhi->getSymbol() != entrylo->getSymbol())
      return 0;		// Not from the same symbol
  }

  bool isWhole = false;
  list<PcodeOp *>::const_iterator iter;
  for(iter=whole->beginDescend();iter!=whole->endDescend();++iter) {
    TypeOp *typeop = (*iter)->getOpcode();
    // Categorize op as "reading a logical whole"
    if (typeop->isArithmeticOp() || typeop->isFloatingPointOp()) {
      isWhole = true;
      break;
    }
  }
  if (!isWhole)
    return 0;
  vnhi->setPrecisHi();
  vnlo->setPrecisLo();
  return 1;
}

int4 RuleDoubleOut::applyOp(PcodeOp *op,Funcdata &data)

{
  Varnode *vnhi= op->getIn(0);
  Varnode *vnlo = op->getIn(1);

  // Currently this only implements collapsing input varnodes read by CPUI_PIECE
  // So we put the test for this particular case early
  if (!vnhi->isInput() || !vnlo->isInput())
    return 0;
  if (!vnhi->isPersist() || !vnlo->isPersist())
    return 0;

  if (!vnhi->isPrecisHi() || !vnlo->isPrecisLo()) {
    return attemptMarking(vnhi,vnlo,op);
  }
  if (data.hasUnreachableBlocks()) return 0;

  Address addr;
  if (!SplitVarnode::isAddrTiedContiguous(vnlo, vnhi, addr))
    return 0;
  data.combineInputVarnodes(vnhi,vnlo);
  return 1;
}

/// \brief Scan for conflicts between two LOADs or STOREs that would prevent them from being combined
///
/// The PcodeOps must be in the same basic block.  Each PcodeOp that falls in between is examined
/// to determine if it writes to the same address space as the LOADs or STOREs, which indicates that
/// combining isn't possible.  If the LOADs and STOREs can be combined, the later of the two PcodeOps
/// is returned, otherwise null is returned.
///
/// In the case of STORE ops, an extra container for INDIRECT PcodeOps is passed in.  INDIRECTs that
/// are caused by the STORE ops themselves are collected in the container.
/// \param op1 is a given LOAD or STORE
/// \param op2 is the other given LOAD or STORE
/// \param spc is the address space referred to by the LOAD/STOREs
/// \param indirects if non-null is used to collect INDIRECTs caused by STOREs
PcodeOp *RuleDoubleLoad::noWriteConflict(PcodeOp *op1,PcodeOp *op2,AddrSpace *spc,vector<PcodeOp *> *indirects)

{
  const BlockBasic *bb = op1->getParent();

  // Force the two ops to be in the same basic block
  if (bb != op2->getParent()) return (PcodeOp *)0;
  if (op2->getSeqNum().getOrder() < op1->getSeqNum().getOrder()) {
    PcodeOp *tmp = op2;
    op2 = op1;
    op1 = tmp;
  }
  PcodeOp *startop = op1;
  if (op1->code() == CPUI_STORE) {
    // Extend the range of PcodeOps to include any CPUI_INDIRECTs associated with the initial STORE
    PcodeOp *tmpOp = startop->previousOp();
    while(tmpOp != (PcodeOp *)0 && tmpOp->code() == CPUI_INDIRECT) {
      startop = tmpOp;
      tmpOp = tmpOp->previousOp();
    }
  }
  list<PcodeOp *>::iterator iter = startop->getBasicIter();
  list<PcodeOp *>::iterator enditer = op2->getBasicIter();

  while(iter != enditer) {
    PcodeOp *curop = *iter;
    Varnode *outvn;
    PcodeOp *affector;
    ++iter;
    if (curop == op1) continue;
    switch(curop->code()) {
    case CPUI_STORE:
      if (curop->getIn(0)->getSpaceFromConst() == spc)
	return (PcodeOp *)0;	// Don't go any further trying to resolve alias
      break;
    case CPUI_INDIRECT:
      affector = PcodeOp::getOpFromConst(curop->getIn(1)->getAddr());
      if (affector == op1 || affector == op2) {
	if (indirects != (vector<PcodeOp *> *)0)
	  indirects->push_back(curop);
      }
      else {
	if (curop->getOut()->getSpace() == spc)
	  return (PcodeOp *)0;
      }
      break;
    case CPUI_CALL:
    case CPUI_CALLIND:
    case CPUI_CALLOTHER:
    case CPUI_RETURN:
    case CPUI_BRANCH:
    case CPUI_CBRANCH:
    case CPUI_BRANCHIND:
      return (PcodeOp *)0;
    default:
      outvn = curop->getOut();
      if (outvn != (Varnode *)0) {
	if (outvn->getSpace() == spc)
	  return (PcodeOp *)0;
      }
      break;
    }
  }
  return op2;
}

void RuleDoubleLoad::getOpList(vector<uint4> &oplist) const

{
  oplist.push_back(CPUI_PIECE);
}

int4 RuleDoubleLoad::applyOp(PcodeOp *op,Funcdata &data)

{
  PcodeOp *loadlo,*loadhi;	// Load from lowest address, highest (NOT significance)
  AddrSpace *spc;
  int4 size;

  Varnode *piece0 = op->getIn(0);
  Varnode *piece1 = op->getIn(1);
  if (!piece0->isWritten()) return 0;
  if (!piece1->isWritten()) return 0;
  PcodeOp *load1 = piece1->getDef();
  if (load1->code() != CPUI_LOAD) return false;
  PcodeOp *load0 = piece0->getDef();
  OpCode opc = load0->code();
  int4 offset = 0;
  if (opc == CPUI_SUBPIECE) {
    // Check for 2 LOADs but most significant part of most significant LOAD is discarded
    if (load0->getIn(1)->getOffset() != 0) return false;
    Varnode *vn0 = load0->getIn(0);
    if (!vn0->isWritten()) return false;
    offset = vn0->getSize() - piece0->getSize();
    load0 = vn0->getDef();
    opc = load0->code();
  }
  if (opc != CPUI_LOAD)
    return false;
  if (!SplitVarnode::testContiguousPointers(load0,load1,loadlo,loadhi,spc))
    return 0;

  size = piece0->getSize() + piece1->getSize();
  PcodeOp *latest = noWriteConflict(loadlo,loadhi,spc,(vector<PcodeOp *> *)0);
  if (latest == (PcodeOp *)0) return 0; // There was a conflict

  // Create new load op that combines the two smaller loads
  PcodeOp *newload = data.newOp(2,latest->getAddr());
  Varnode *vnout = data.newUniqueOut(size,newload);
  Varnode *spcvn = data.newVarnodeSpace(spc);
  data.opSetOpcode(newload,CPUI_LOAD);
  data.opSetInput(newload,spcvn,0);
  Varnode *addrvn = loadlo->getIn(1);
  if (spc->isBigEndian() && offset != 0) {
    // If the most significant part of LOAD is discarded, we need to add discard amount to pointer
    PcodeOp *newadd = data.newOp(2,latest->getAddr());
    Varnode *addout = data.newUniqueOut(addrvn->getSize(),newadd);
    data.opSetOpcode(newadd,CPUI_INT_ADD);
    data.opSetInput(newadd,addrvn,0);
    data.opSetInput(newadd,data.newConstant(addrvn->getSize(), offset),1);
    data.opInsertAfter(newadd,latest);
    addrvn = addout;
    latest = newadd;
  }
  data.opSetInput(newload,addrvn,1);
  // We need to guarantee that -newload- reads -addrvn- after
  // it has been defined. So insert it after the latest.
  data.opInsertAfter(newload,latest);

  // Change the concatenation to a copy from the big load
  data.opRemoveInput(op,1);
  data.opSetOpcode(op,CPUI_COPY);
  data.opSetInput(op,vnout,0);

  return 1;
}

void RuleDoubleStore::getOpList(vector<uint4> &oplist) const

{
  oplist.push_back(CPUI_STORE);
}

int4 RuleDoubleStore::applyOp(PcodeOp *op,Funcdata &data)

{
  PcodeOp *storelo,*storehi;
  AddrSpace *spc;

  Varnode *vnlo = op->getIn(2);
  if (!vnlo->isPrecisLo()) return 0;
  if (!vnlo->isWritten()) return 0;
  PcodeOp *subpieceOpLo = vnlo->getDef();
  if (subpieceOpLo->code() != CPUI_SUBPIECE) return 0;
  if (subpieceOpLo->getIn(1)->getOffset() != 0) return 0;
  Varnode *whole = subpieceOpLo->getIn(0);
  if (whole->isFree()) return 0;
  list<PcodeOp *>::const_iterator iter;
  for(iter=whole->beginDescend();iter!=whole->endDescend();++iter) {
    PcodeOp *subpieceOpHi = *iter;
    if (subpieceOpHi->code() != CPUI_SUBPIECE) continue;
    if (subpieceOpHi == subpieceOpLo) continue;
    int4 offset = (int4)subpieceOpHi->getIn(1)->getOffset();
    if (offset != vnlo->getSize()) continue;
    Varnode *vnhi = subpieceOpHi->getOut();
    if (!vnhi->isPrecisHi()) continue;
    if (vnhi->getSize() != whole->getSize() - offset) continue;
    list<PcodeOp *>::const_iterator iter2;
    for(iter2=vnhi->beginDescend();iter2!=vnhi->endDescend();++iter2) {
      PcodeOp *storeOp2 = *iter2;
      if (storeOp2->code() != CPUI_STORE) continue;
      if (storeOp2->getIn(2) != vnhi) continue;
      if (SplitVarnode::testContiguousPointers(storeOp2, op, storelo, storehi, spc)) {
	vector<PcodeOp *> indirects;
	PcodeOp *latest = RuleDoubleLoad::noWriteConflict(storelo,storehi,spc,&indirects);
	if (latest == (PcodeOp *)0) continue;	// There was a conflict
	if (!testIndirectUse(storelo, storehi, indirects)) continue;
	// Create new STORE op that combines the two smaller STOREs
	PcodeOp *newstore = data.newOp(3,latest->getAddr());
	Varnode *spcvn = data.newVarnodeSpace(spc);
	data.opSetOpcode(newstore,CPUI_STORE);
	data.opSetInput(newstore,spcvn,0);
	Varnode *addrvn = storelo->getIn(1);
	if (addrvn->isConstant())
	  addrvn = data.newConstant(addrvn->getSize(),addrvn->getOffset());
	data.opSetInput(newstore,addrvn,1);
	data.opSetInput(newstore,whole,2);
	// We need to guarantee that -newstore- reads -addrvn- after
	// it has been defined. So insert it after the latest.
	data.opInsertAfter(newstore,latest);
	data.opDestroy(op);		// Get rid of the original STOREs
	data.opDestroy(storeOp2);
	reassignIndirects(data, newstore, indirects);
	return 1;
      }
    }
  }
  return 0;
}

/// \brief Test if output Varnodes from a list of PcodeOps are used anywhere within a range of PcodeOps
///
/// The range of PcodeOps bounded by given starting and ending PcodeOps.  An output Varnode is
/// used within the range if there is a PcodeOp in the range that takes the Varnode as input.
/// \param op1 is the given starting PcodeOp of the range
/// \param op2 is the given ending PcodeOp of the range
/// \param indirects is the list of PcodesOps whose output are tested
/// \return \b true if no output in the list is used in the range
bool RuleDoubleStore::testIndirectUse(PcodeOp *op1,PcodeOp *op2,const vector<PcodeOp *> &indirects)

{
  if (op2->getSeqNum().getOrder() < op1->getSeqNum().getOrder()) {
    PcodeOp *tmp = op2;
    op2 = op1;
    op1 = tmp;
  }
  for(int4 i=0;i<indirects.size();++i) {
    Varnode *outvn = indirects[i]->getOut();
    list<PcodeOp *>::const_iterator iter;
    int4 usecount = 0;
    int4 usebyop2 = 0;
    for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
      PcodeOp *op = *iter;
      usecount += 1;
      if (op->getParent() != op1->getParent()) continue;
      if (op->getSeqNum().getOrder() < op1->getSeqNum().getOrder()) continue;
      if (op->getSeqNum().getOrder() > op2->getSeqNum().getOrder()) continue;
      // Its likely that INDIRECTs from the first STORE feed INDIRECTs for the second STORE
      if (op->code() == CPUI_INDIRECT && op2 == PcodeOp::getOpFromConst(op->getIn(1)->getAddr())) {
	usebyop2 += 1;	// Note this pairing
	continue;
      }
      return false;
    }
    // As an INDIRECT whose output Varnode feeds into later INDIRECTs must be removed, we need the following test.
    // If some uses of the output feed into later INDIRECTs, but not ALL do, then return false
    if (usebyop2 > 0 && usecount != usebyop2)
      return false;
    if (usebyop2 > 1)
      return false;
  }
  return true;
}

/// \brief Reassign INDIRECTs to a new given STORE
///
/// The INDIRECTs are associated with old STOREs that are being removed.
/// Each INDIRECT is moved from its position near the old STORE to be near the new STORE and
/// the affect iop operand is set to point at the new STORE.
/// \param data is the function owning the INDIRECTs
/// \param newStore is the given new STORE PcodeOp
/// \param indirects is the list of INDIRECT PcodeOps to reassign
void RuleDoubleStore::reassignIndirects(Funcdata &data,PcodeOp *newStore,const vector<PcodeOp *> &indirects)

{
  // Search for INDIRECT pairs.  The earlier is deleted.  The later gains the earlier's input.
  for(int4 i=0;i<indirects.size();++i) {
    PcodeOp *op = indirects[i];
    op->setMark();
    Varnode *vn = op->getIn(0);
    if (!vn->isWritten()) continue;
    PcodeOp *earlyop = vn->getDef();
    if (earlyop->isMark()) {
      data.opSetInput(op,earlyop->getIn(0),0);	// Grab the earlier op's input, replacing the use of its output
      data.opDestroy(earlyop);
    }
  }
  for(int4 i=0;i<indirects.size();++i) {
    PcodeOp *op = indirects[i];
    op->clearMark();
    if (op->isDead()) continue;
    data.opUninsert(op);
    data.opInsertBefore(op,newStore);		// Move the INDIRECT to the new STORE
    data.opSetInput(op,data.newVarnodeIop(newStore),1);	// Assign the INDIRECT to the new STORE
  }
}

} // End namespace ghidra
