/* ###
 * 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 "funcdata.hh"
#include "flow.hh"

namespace ghidra {

// Funcdata members pertaining directly to ops

/// \param op is the given PcodeOp
/// \param opc is the op-code to set
void Funcdata::opSetOpcode(PcodeOp *op,OpCode opc)

{
#ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
  obank.changeOpcode(op, glb->inst[opc] );
}

/// \param op is the given CPUI_RETURN op
/// \param flag is one of \e halt, \e badinstruction, \e unimplemented, \e noreturn, or \e missing.
void Funcdata::opMarkHalt(PcodeOp *op,uint4 flag)

{
  if (op->code() != CPUI_RETURN)
    throw LowlevelError("Only RETURN pcode ops can be marked as halt");
  flag &= (PcodeOp::halt|PcodeOp::badinstruction|
	   PcodeOp::unimplemented|PcodeOp::noreturn|
	   PcodeOp::missing);
  if (flag == 0)
    throw LowlevelError("Bad halt flag");
  op->setFlag(flag);
}

/// The output Varnode becomes \e free but is not immediately deleted.
/// \param op is the given PcodeOp
void Funcdata::opUnsetOutput(PcodeOp *op)

{
  Varnode *vn;

  vn = op->getOut();
  if (vn == (Varnode *)0) return; // Nothing to do
#ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
  op->setOutput((Varnode *)0); // This must come before make_free
  vbank.makeFree(vn);
  vn->clearCover();
}

/// \param op is the specific PcodeOp
/// \param vn is the output Varnode to set
void Funcdata::opSetOutput(PcodeOp *op,Varnode *vn)

{
  if (vn == op->getOut()) return; // Already set to this vn
#ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
  if (op->getOut() != (Varnode *)0) {
    opUnsetOutput(op);
  }

  if (vn->getDef() != (PcodeOp *)0)	// If this varnode is already an output
    opUnsetOutput(vn->getDef());
  vn = vbank.setDef(vn,op);
  setVarnodeProperties(vn);
  op->setOutput(vn);
}

/// The input Varnode is unlinked from the op.
/// \param op is the given PcodeOp
/// \param slot is the input slot to clear
void Funcdata::opUnsetInput(PcodeOp *op,int4 slot)

{
  Varnode *vn = op->getIn(slot);

  vn->eraseDescend(op);
  op->clearInput(slot);		// Must be called AFTER descend_erase
}

/// \param op is the given PcodeOp
/// \param vn is the operand Varnode to set
/// \param slot is the input slot where the Varnode is placed
void Funcdata::opSetInput(PcodeOp *op,Varnode *vn,int4 slot)

{
  if (vn == op->getIn(slot)) return; // Already set to this vn
  if (vn->isConstant()) {	// Constants should have only one descendant
    if (!vn->hasNoDescend())
      if (!vn->isSpacebase()) {	// Unless they are a spacebase
	Varnode *cvn = newConstant(vn->getSize(),vn->getOffset());
	cvn->copySymbol(vn);
	vn = cvn;
      }
  }
#ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
  if (op->getIn(slot) != (Varnode *)0)
    opUnsetInput(op,slot);

  vn->addDescend(op);		// Add this op to list of vn's descendants
  op->setInput(vn,slot);	// op must be up to date AFTER calling descend_add
}

/// This is convenience method that is more efficient than call opSetInput() twice.
/// \param op is the given PcodeOp
/// \param slot1 is the first input slot being switched
/// \param slot2 is the second input slot
void Funcdata::opSwapInput(PcodeOp *op,int4 slot1,int4 slot2)

{
#ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
  Varnode *tmp = op->getIn(slot1);
  op->setInput(op->getIn(slot2),slot1);
  op->setInput(tmp,slot2);
}

/// \brief Insert the given PcodeOp at specific point in a basic block
///
/// The PcodeOp is removed from the \e dead list and is inserted \e immediately before
/// the specified iterator.
/// \param op is the given PcodeOp
/// \param bl is the basic block being inserted into
/// \param iter indicates exactly where the op is inserted
void Funcdata::opInsert(PcodeOp *op,BlockBasic *bl,list<PcodeOp *>::iterator iter)

{
#ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
  obank.markAlive(op);
  bl->insert(iter,op);
}

/// The op is taken out of its basic block and put into the dead list. If the removal
/// is permanent the input and output Varnodes should be unset.
/// \param op is the given PcodeOp
void Funcdata::opUninsert(PcodeOp *op)

{
  #ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
  obank.markDead(op);
  op->getParent()->removeOp(op);
}

/// The op is extricated from all its Varnode connections to the functions data-flow and
/// removed from its basic block. This will \e not change block connections.  The PcodeOp
/// objects remains in the \e dead list.
/// \param op is the given PcodeOp
void Funcdata::opUnlink(PcodeOp *op)

{
  int4 i;
#ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
				// Unlink input and output varnodes
  opUnsetOutput(op);
  for(i=0;i<op->numInput();++i)
    opUnsetInput(op,i);
  if (op->getParent() != (BlockBasic *)0) // Remove us from basic block
    opUninsert(op);
}

/// All input and output Varnodes to the op are destroyed (their object resources freed),
/// and the op is permanently moved to the \e dead list.
/// To call this routine, make sure that either:
///   - The op has no output
///   - The op's output has no descendants
///   - or all descendants of output are also going to be destroyed
///
/// \param op is the given PcodeOp
void Funcdata::opDestroy(PcodeOp *op)

{
  #ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif

  if (op->getOut() != (Varnode *)0)
    destroyVarnode(op->getOut());
  for(int4 i=0;i<op->numInput();++i) {
    Varnode *vn = op->getIn(i);
    if (vn != (Varnode *)0)
      opUnsetInput(op,i);
  }
  if (op->getParent() != (BlockBasic *)0) {
    obank.markDead(op);
    op->getParent()->removeOp(op);
  }
}

/// The given PcodeOp is always removed.  PcodeOps are recursively removed, if the only data-flow
/// path of their output is to the given op, and they are not a CALL or are otherwise special.
/// \param op is the given PcodeOp to remove
/// \param scratch is scratch space for holding PcodeOps being examined
void Funcdata::opDestroyRecursive(PcodeOp *op,vector<PcodeOp *> &scratch)

{
  scratch.clear();
  scratch.push_back(op);
  int4 pos = 0;
  while(pos < scratch.size()) {
    op = scratch[pos];
    pos += 1;
    for(int4 i=0;i<op->numInput();++i) {
      Varnode *vn = op->getIn(i);
      if (!vn->isWritten() || vn->isAutoLive()) continue;
      if (vn->loneDescend() == (PcodeOp *)0) continue;
      PcodeOp *defOp = vn->getDef();
      if (defOp->isCall() || defOp->isIndirectSource()) continue;
      scratch.push_back(defOp);
    }
    opDestroy(op);
  }
}

/// This is a specialized routine for deleting an op during flow generation that has
/// been replaced by something else.  The op is expected to be \e dead with none of its inputs
/// or outputs linked to anything else.  Both the PcodeOp and all the input/output Varnodes are destroyed.
/// \param op is the given PcodeOp
void Funcdata::opDestroyRaw(PcodeOp *op)

{
  for(int4 i=0;i<op->numInput();++i)
    destroyVarnode(op->getIn(i));
  if (op->getOut() != (Varnode *)0)
    destroyVarnode(op->getOut());
  obank.destroy(op);
}

/// All previously existing input Varnodes are unset.  The input slots for the
/// op are resized and then filled in from the specified array.
/// \param op is the given PcodeOp to set
/// \param vvec is the specified array of new input Varnodes
void Funcdata::opSetAllInput(PcodeOp *op,const vector<Varnode *> &vvec)

{
  int4 i;

#ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
  for(i=0;i<op->numInput();++i)
    if (op->getIn(i) != (Varnode *)0)
      opUnsetInput(op,i);

  op->setNumInputs( vvec.size() );

  for(i=0;i<op->numInput();++i)
    opSetInput(op,vvec[i],i);
}

/// The Varnode in the specified slot is unlinked from the op and the slot itself
/// is removed. The slot index for any remaining input Varnodes coming after the
/// specified slot is decreased by one.
/// \param op is the given PcodeOp
/// \param slot is the index of the specified slot to remove
void Funcdata::opRemoveInput(PcodeOp *op,int4 slot)

{
#ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
  opUnsetInput(op,slot);
  op->removeInput(slot);
}

/// The given Varnode is set into the given operand slot. Any existing input Varnodes
/// with slot indices equal to or greater than the specified slot are pushed into the
/// next slot.
/// \param op is the given PcodeOp
/// \param vn is the given Varnode to insert
/// \param slot is the input index to insert at
void Funcdata::opInsertInput(PcodeOp *op,Varnode *vn,int4 slot)

{
#ifdef OPACTION_DEBUG
  if (opactdbg_active)
    debugModCheck(op);
#endif
  op->insertInput(slot);
  opSetInput(op,vn,slot);
}

/// \param inputs is the number of operands the new op will have
/// \param pc is the Address associated with the new op
/// \return the new PcodeOp
PcodeOp *Funcdata::newOp(int4 inputs,const Address &pc)

{
  return obank.create(inputs,pc);
}

/// This method is typically used for cloning.
/// \param inputs is the number of operands the new op will have
/// \param sq is the sequence number (Address and sub-index) of the new op
/// \return the new PcodeOp
PcodeOp *Funcdata::newOp(int4 inputs,const SeqNum &sq)

{
  return obank.create(inputs,sq);
}

/// The given PcodeOp is inserted \e immediately before the \e follow op except:
///  - MULTIEQUALS in a basic block all occur first
///  - INDIRECTs occur immediately before their op
///  - a branch op must be the very last op in a basic block
///
/// \param op is the given PcodeOp to insert
/// \param follow is the op to insert before
void Funcdata::opInsertBefore(PcodeOp *op,PcodeOp *follow)

{
  list<PcodeOp *>::iterator iter = follow->getBasicIter();
  BlockBasic *parent = follow->getParent();

  if (op->code() != CPUI_INDIRECT) {
  // There should not be an INDIRECT immediately preceding op
    PcodeOp *previousop;
    while(iter != parent->beginOp()) {
      --iter;
      previousop = *iter;
      if (previousop->code() != CPUI_INDIRECT) {
	++iter;
	break;
      }
    }
  }
  opInsert(op,parent,iter);
}

/// The given PcodeOp is inserted \e immediately after the \e prev op except:
///  - MULTIEQUALS in a basic block all occur first
///  - INDIRECTs occur immediately before their op
///  - a branch op must be the very last op in a basic block
///
/// \param op is the given PcodeOp to insert
/// \param prev is the op to insert after
void Funcdata::opInsertAfter(PcodeOp *op,PcodeOp *prev)

{
  if (prev->isMarker()) {
    if (prev->code() == CPUI_INDIRECT) {
      Varnode *invn = prev->getIn(1);
      if (invn->getSpace()->getType()==IPTR_IOP) {
	PcodeOp *targOp = PcodeOp::getOpFromConst(invn->getAddr()); // Store or call
	if (!targOp->isDead())
	  prev = targOp;
      }
    }
  }
  list<PcodeOp *>::iterator iter = prev->getBasicIter();
  BlockBasic *parent = prev->getParent();

  iter++;

  if (op->code() != CPUI_MULTIEQUAL) {
  // There should not be a MULTIEQUAL immediately after op
    PcodeOp *nextop;
    while(iter != parent->endOp()) {
      nextop = *iter;
      ++iter;
      if (nextop->code() != CPUI_MULTIEQUAL) {
	--iter;
	break;
      }
    }
  }
  opInsert(op,prev->getParent(),iter);
}

/// The given PcodeOp is inserted as the \e first op in the basic block except:
///  - MULTIEQUALS in a basic block all occur first
///  - INDIRECTs occur immediately before their op
///  - a branch op must be the very last op in a basic block
///
/// \param op is the given PcodeOp to insert
/// \param bl is the basic block to insert into
void Funcdata::opInsertBegin(PcodeOp *op,BlockBasic *bl)

{
  list<PcodeOp *>::iterator iter = bl->beginOp();
  
  if (op->code()!=CPUI_MULTIEQUAL) {
    while(iter != bl->endOp()) {
      if ((*iter)->code() != CPUI_MULTIEQUAL)
	break;
      ++iter;
    }
  }
  opInsert(op,bl,iter);
}

/// The given PcodeOp is inserted as the \e last op in the basic block except:
///  - MULTIEQUALS in a basic block all occur first
///  - INDIRECTs occur immediately before their op
///  - a branch op must be the very last op in a basic block
///
/// \param op is the given PcodeOp to insert
/// \param bl is the basic block to insert into
void Funcdata::opInsertEnd(PcodeOp *op,BlockBasic *bl)

{
  list<PcodeOp *>::iterator iter = bl->endOp();

  if (iter != bl->beginOp()) {
    --iter;
    if (!(*iter)->isFlowBreak())
      ++iter;
  }
  opInsert(op,bl,iter);
}

/// \brief Create an INT_ADD PcodeOp calculating an offset to the \e spacebase register.
///
/// The \e spacebase register is looked up for the given address space, or an optional previously
/// existing register Varnode can be provided. An insertion point op must be provided,
/// and newly generated ops can come either before or after this insertion point.
/// \param spc is the given address space
/// \param off is the offset to calculate relative to the \e spacebase register
/// \param op is the insertion point PcodeOp
/// \param stackptr is the \e spacebase register Varnode (if available)
/// \param insertafter is \b true if new ops are inserted \e after the insertion point
/// \return the \e unique space Varnode holding the calculated offset
Varnode *Funcdata::createStackRef(AddrSpace *spc,uintb off,PcodeOp *op,Varnode *stackptr,bool insertafter)

{
  PcodeOp *addop;
  Varnode *addout;
  int4 addrsize;

  // Calculate CURRENT stackpointer as base for relative offset
  if (stackptr == (Varnode *)0)	// If we are not reusing an old reference to the stack pointer
    stackptr = newSpacebasePtr(spc); // create a new reference
  addrsize = stackptr->getSize();
  addop = newOp(2,op->getAddr());
  opSetOpcode(addop,CPUI_INT_ADD);
  addout = newUniqueOut(addrsize,addop);
  opSetInput(addop,stackptr,0);
  off = AddrSpace::byteToAddress(off,spc->getWordSize());
  opSetInput(addop,newConstant(addrsize,off),1);
  if (insertafter)
    opInsertAfter(addop,op);
  else
    opInsertBefore(addop,op);

  AddrSpace *containerid = spc->getContain();
  SegmentOp *segdef = glb->userops.getSegmentOp(containerid->getIndex());

  if (segdef != (SegmentOp *)0) {
    PcodeOp *segop = newOp(3,op->getAddr());
    opSetOpcode(segop,CPUI_SEGMENTOP);
    Varnode *segout = newUniqueOut(containerid->getAddrSize(),segop);
    opSetInput(segop,newVarnodeSpace(containerid),0);
    opSetInput(segop,newConstant(segdef->getBaseSize(),0),1);
    opSetInput(segop,addout,2);
    opInsertAfter(segop,addop); // Make sure -segop- comes after -addop- regardless if before/after -op-
    addout = segout;
  }

  return addout;
}

/// \brief Create a STORE expression at an offset relative to a \e spacebase register for a given address space
///
/// The \e spacebase register is looked up for the given address space. An insertion point
/// op must be provided, and newly generated ops can come either before or after this insertion point.
/// The Varnode value being stored must still be set on the returned PcodeOp.
/// \param spc is the given address space
/// \param off is the offset to calculate relative to the \e spacebase register
/// \param op is the insertion point PcodeOp
/// \param insertafter is \b true if new ops are inserted \e after the insertion point
/// \return the STORE PcodeOp
PcodeOp *Funcdata::opStackStore(AddrSpace *spc,uintb off,PcodeOp *op,bool insertafter)

{ // Create pcode sequence that stores a value at an offset relative to a spacebase
  // -off- is the offset, -size- is the size of the value
  // The sequence is inserted before/after -op- based on whether -insertafter- is false/true
  // Return the store op
  Varnode *addout;
  PcodeOp *storeop;

  // Calculate CURRENT stackpointer as base for relative offset
  addout = createStackRef(spc,off,op,(Varnode *)0,insertafter);

  storeop = newOp(3,op->getAddr());
  opSetOpcode(storeop,CPUI_STORE);

  opSetInput(storeop,newVarnodeSpace(spc->getContain()),0);
  opSetInput(storeop,addout,1);
  opInsertAfter(storeop,addout->getDef()); // STORE comes after stack building op, regardless of -insertafter-
  return storeop;
}

/// \brief Create a LOAD expression at an offset relative to a \e spacebase register for a given address space
///
/// The \e spacebase register is looked up for the given address space, or an optional previously
/// existing register Varnode can be provided. An insertion point op must be provided,
/// and newly generated ops can come either before or after this insertion point.
/// \param spc is the given address space
/// \param off is the offset to calculate relative to the \e spacebase register
/// \param sz is the size of the desire LOAD in bytes
/// \param op is the insertion point PcodeOp
/// \param stackref is the \e spacebase register Varnode (if available)
/// \param insertafter is \b true if new ops are inserted \e after the insertion point
/// \return the \e unique space Varnode holding the result of the LOAD
Varnode *Funcdata::opStackLoad(AddrSpace *spc,uintb off,uint4 sz,PcodeOp *op,Varnode *stackref,bool insertafter)

{
    Varnode *addout = createStackRef(spc,off,op,stackref,insertafter);
    PcodeOp *loadop = newOp(2,op->getAddr());
    opSetOpcode(loadop,CPUI_LOAD);
    opSetInput(loadop,newVarnodeSpace(spc->getContain()),0);
    opSetInput(loadop,addout,1);
    Varnode *res = newUniqueOut(sz,loadop);
    opInsertAfter(loadop,addout->getDef()); // LOAD comes after stack building op, regardless of -insertafter-
    return res;
}

/// \brief Construct the boolean negation of a given boolean Varnode into a temporary register
///
/// \param vn is the given Varnode
/// \param op is the point at which to insert the BOOL_NEGATE op
/// \param insertafter is \b true if the BOOL_NEGATE is inserted after, otherwise its inserted before
/// \return the result Varnode
Varnode *Funcdata::opBoolNegate(Varnode *vn,PcodeOp *op,bool insertafter)

{
  PcodeOp *negateop = newOp(1,op->getAddr());
  opSetOpcode(negateop,CPUI_BOOL_NEGATE);
  Varnode *resvn = newUniqueOut(1,negateop);
  opSetInput(negateop,vn,0);
  if (insertafter)
    opInsertAfter(negateop,op);
  else
    opInsertBefore(negateop,op);
  return resvn;
}

/// Convert the given CPUI_PTRADD into the equivalent CPUI_INT_ADD.  This may involve inserting a
/// CPUI_INT_MULT PcodeOp. If finalization is requested and a new PcodeOp is needed, the output
/// Varnode is marked as \e implicit and has its data-type set
/// \param op is the given PTRADD
/// \param finalize is \b true if finalization is needed for any new PcodeOp
void Funcdata::opUndoPtradd(PcodeOp *op,bool finalize)

{
  Varnode *multVn = op->getIn(2);
  int4 multSize = multVn->getOffset(); // Size the PTRADD thinks we are pointing

  opRemoveInput(op,2);
  opSetOpcode(op,CPUI_INT_ADD);
  if (multSize == 1) return;	// If no multiplier, we are done
  Varnode *offVn = op->getIn(1);
  if (offVn->isConstant()) {
    uintb newVal = multSize * offVn->getOffset();
    newVal &= calc_mask(offVn->getSize());
    Varnode *newOffVn = newConstant(offVn->getSize(), newVal);
    if (finalize)
      newOffVn->updateType(offVn->getTypeReadFacing(op));
    opSetInput(op,newOffVn,1);
    return;
  }
  PcodeOp *multOp = newOp(2,op->getAddr());
  opSetOpcode(multOp,CPUI_INT_MULT);
  Varnode *addVn = newUniqueOut(offVn->getSize(),multOp);
  if (finalize) {
    addVn->updateType(multVn->getType());
    addVn->setImplied();
  }
  opSetInput(multOp,offVn,0);
  opSetInput(multOp,multVn,1);
  opSetInput(op,addVn,1);
  opInsertBefore(multOp,op);
}

/// Make a clone of the given PcodeOp, copying control-flow properties as well.  The data-type
/// is \e not cloned.
/// \param op is the PcodeOp to clone
/// \param seq is the (possibly custom) sequence number to associate with the clone
/// \return the cloned PcodeOp
PcodeOp *Funcdata::cloneOp(const PcodeOp *op,const SeqNum &seq)

{
  PcodeOp *newop = newOp(op->numInput(),seq);
  opSetOpcode(newop,op->code());
  uint4 fl = op->flags & (PcodeOp::startmark | PcodeOp::startbasic);
  newop->setFlag(fl);
  if (op->getOut() != (Varnode *)0)
    opSetOutput(newop,cloneVarnode(op->getOut()));
  for(int4 i=0;i<op->numInput();++i)
    opSetInput(newop,cloneVarnode(op->getIn(i)),i);
  return newop;
}

/// Return the first CPUI_RETURN operation that is not dead or an artificial halt
/// \return a representative CPUI_RETURN op or NULL if there are none
PcodeOp *Funcdata::getFirstReturnOp(void) const

{
  list<PcodeOp *>::const_iterator iter,iterend;
  iterend = endOp(CPUI_RETURN);
  for(iter=beginOp(CPUI_RETURN);iter!=iterend;++iter) {
    PcodeOp *retop = *iter;
    if (retop->isDead()) continue;
    if (retop->getHaltType()!=0) continue;
    return retop;
  }
  return (PcodeOp *)0;
}

/// \brief Create new PcodeOp with 2 or 3 given operands
///
/// The new op will have a \e unique space output Varnode and will be inserted before
/// the given \e follow op.
/// \param follow is the \e follow up to insert the new PcodeOp before
/// \param opc is the op-code of the new PcodeOp
/// \param in1 is the first operand
/// \param in2 is the second operand
/// \param in3 is the optional third param
/// \return the new PcodeOp
PcodeOp *Funcdata::newOpBefore(PcodeOp *follow,OpCode opc,Varnode *in1,Varnode *in2,Varnode *in3)

{
  PcodeOp *newop;
  int4 sz;

  sz = (in3 == (Varnode *)0) ? 2 : 3;
  newop = newOp(sz,follow->getAddr());
  opSetOpcode(newop,opc);
  newUniqueOut(in1->getSize(),newop);
  opSetInput(newop,in1,0);
  opSetInput(newop,in2,1);
  if (sz==3)
    opSetInput(newop,in3,2);
  opInsertBefore(newop,follow);
  return newop;
}

/// \brief Create a new CPUI_INDIRECT around a PcodeOp with an indirect effect
///
/// Typically this is used to annotate data-flow, for the given storage range, passing
/// through a CALL or STORE. An output Varnode is automatically created.
/// \param indeffect is the PcodeOp with the indirect effect
/// \param addr is the starting address of the storage range to protect
/// \param sz is the number of bytes in the storage range
/// \param extraFlags are extra boolean properties to put on the INDIRECT
/// \return the new CPUI_INDIRECT op
PcodeOp *Funcdata::newIndirectOp(PcodeOp *indeffect,const Address &addr,int4 sz,uint4 extraFlags)

{
  Varnode *newin;
  PcodeOp *newop;

  newin = newVarnode(sz,addr);
  newop = newOp(2,indeffect->getAddr());
  newop->flags |= extraFlags;
  newVarnodeOut(sz,addr,newop);
  opSetOpcode(newop,CPUI_INDIRECT);
  opSetInput(newop,newin,0);
  opSetInput(newop,newVarnodeIop(indeffect),1);
  opInsertBefore(newop,indeffect);
  return newop;
}

/// \brief Build a CPUI_INDIRECT op that \e indirectly \e creates a Varnode
///
/// An \e indirectly \e created Varnode effectively has no data-flow before the INDIRECT op
/// that defines it, and the value contained by the Varnode is not explicitly calculable.
/// The new Varnode is allocated with a given storage range.
/// \param indeffect is the p-code causing the indirect effect
/// \param addr is the starting address of the given storage range
/// \param sz is the number of bytes in the storage range
/// \param possibleout is \b true if the output should be treated as a \e directwrite.
/// \return the new CPUI_INDIRECT op
PcodeOp *Funcdata::newIndirectCreation(PcodeOp *indeffect,const Address &addr,int4 sz,bool possibleout)

{
  Varnode *newout,*newin;
  PcodeOp *newop;

  newin = newConstant(sz,0);
  newop = newOp(2,indeffect->getAddr());
  newop->flags |= PcodeOp::indirect_creation;
  newout = newVarnodeOut(sz,addr,newop);
  if (!possibleout)
    newin->flags |= Varnode::indirect_creation;
  newout->flags |= Varnode::indirect_creation;
  opSetOpcode(newop,CPUI_INDIRECT);
  opSetInput(newop,newin,0);
  opSetInput(newop,newVarnodeIop(indeffect),1);
  opInsertBefore(newop,indeffect);
  return newop;
}

/// Data-flow through the given CPUI_INDIRECT op is marked so that the output Varnode
/// is considered \e indirectly \e created.
/// An \e indirectly \e created Varnode effectively has no data-flow before the INDIRECT op
/// that defines it, and the value contained by the Varnode is not explicitly calculable.
/// \param indop is the given CPUI_INDIRECT op
/// \param possibleOutput is \b true if INDIRECT should be marked as a possible call output
void Funcdata::markIndirectCreation(PcodeOp *indop,bool possibleOutput)

{
  Varnode *outvn = indop->getOut();
  Varnode *in0 = indop->getIn(0);

  indop->flags |= PcodeOp::indirect_creation;
  if (!in0->isConstant())
    throw LowlevelError("Indirect creation not properly formed");
  if (!possibleOutput)
    in0->flags |= Varnode::indirect_creation;
  outvn->flags |= Varnode::indirect_creation;
}

/// \brief Generate raw p-code for the function
///
/// Follow flow from the entry point generating PcodeOps for each instruction encountered.
/// The caller can provide a bounding range that constrains where control can flow to.
/// \param baddr is the beginning of the constraining range
/// \param eaddr is the end of the constraining range
void Funcdata::followFlow(const Address &baddr,const Address &eaddr)

{
  if (!obank.empty()) {
    if ((flags & blocks_generated)==0)
      throw LowlevelError("Function loaded for inlining");
    return;	// Already translated
  }

  uint4 fl = 0;
  fl |= glb->flowoptions;	// Global flow options
  FlowInfo flow(*this,obank,bblocks,qlst);
  flow.setRange(baddr,eaddr);
  flow.setFlags(fl);
  flow.setMaximumInstructions(glb->max_instructions);
  flow.generateOps();
  size = flow.getSize();
  // Cannot keep track of function sizes in general because of non-contiguous functions
  //  glb->symboltab->update_size(name,size);

  flow.generateBlocks();
  flags |= blocks_generated;
  switchOverJumpTables(flow);
  if (flow.hasUnimplemented())
    flags |= unimplemented_present;
  if (flow.hasBadData())
    flags |= baddata_present;
}

/// \brief Generate a clone with truncated control-flow given a partial function
///
/// Existing p-code is cloned from another function whose flow has not been completely
/// followed. Artificial halt operators are inserted wherever flow is incomplete and
/// basic blocks are generated.
/// \param fd is the partial function to clone
/// \param flow is partial function's flow information
void Funcdata::truncatedFlow(const Funcdata *fd,const FlowInfo *flow)

{
  if (!obank.empty())
    throw LowlevelError("Trying to do truncated flow on pre-existing pcode");

  list<PcodeOp *>::const_iterator oiter; // Clone the raw pcode
  for(oiter=fd->obank.beginDead();oiter!=fd->obank.endDead();++oiter)
    cloneOp(*oiter,(*oiter)->getSeqNum());
  obank.setUniqId(fd->obank.getUniqId());

  // Clone callspecs
  for(int4 i=0;i<fd->qlst.size();++i) {
    FuncCallSpecs *oldspec = fd->qlst[i];
    PcodeOp *newop = findOp(oldspec->getOp()->getSeqNum());
    FuncCallSpecs *newspec = oldspec->clone(newop);
    Varnode *invn0 = newop->getIn(0);
    if (invn0->getSpace()->getType() == IPTR_FSPEC) { // Replace embedded pointer to callspec
      Varnode *newvn0 = newVarnodeCallSpecs(newspec);
      opSetInput(newop,newvn0,0);
      deleteVarnode(invn0);
    }
    qlst.push_back(newspec);
  }

  vector<JumpTable *>::const_iterator jiter; // Clone the jumptables
  for(jiter=fd->jumpvec.begin();jiter!=fd->jumpvec.end();++jiter) {
    PcodeOp *indop = (*jiter)->getIndirectOp();
    if (indop == (PcodeOp *)0)	// If indirect op has not been linked, this is probably a jumptable override
      continue;			// that has not been reached by the flow yet, so we ignore/truncate it
    PcodeOp *newop = findOp(indop->getSeqNum());
    if (newop == (PcodeOp *)0)
      throw LowlevelError("Could not trace jumptable across partial clone");
    JumpTable *jtclone = new JumpTable(*jiter);
    jtclone->setIndirectOp(newop);
    jumpvec.push_back(jtclone);
  }

  FlowInfo partialflow(*this,obank,bblocks,qlst,flow); // Clone the flow
  if (partialflow.hasInject())
    partialflow.injectPcode();
  // Clear error reporting flags
  // Keep possible unreachable flag
  partialflow.clearFlags(~((uint4)FlowInfo::possible_unreachable));

  partialflow.generateBlocks(); // Generate basic blocks for partial flow
  flags |= blocks_generated;
}

/// \brief In-line the p-code from another function into \b this function
///
/// Raw PcodeOps for the in-line function are generated and then cloned into
/// \b this function.  Depending on the control-flow complexity of the in-line
/// function, the PcodeOps are injected as if they are all part of the call site
/// address (EZModel), or the PcodeOps preserve their address and extra branch
/// instructions are inserted to integrate control-flow of the in-line into
/// the calling function.
/// \param inlinefd is the function to in-line
/// \param flow is the flow object being injected
/// \param callop is the site of the injection
/// \return 0 for a successful inlining with the easy model, 1 for the hard model, -1 if inlining was not successful
int4 Funcdata::inlineFlow(Funcdata *inlinefd,FlowInfo &flow,PcodeOp *callop)

{
  inlinefd->getArch()->clearAnalysis(inlinefd);
  FlowInfo inlineflow(*inlinefd,inlinefd->obank,inlinefd->bblocks,inlinefd->qlst);
  inlinefd->obank.setUniqId( obank.getUniqId() );

  // Generate the pcode ops to be inlined
  Address baddr(baseaddr.getSpace(),0);
  Address eaddr(baseaddr.getSpace(),~((uintb)0));
  inlineflow.setRange(baddr,eaddr);
  inlineflow.setFlags(FlowInfo::error_outofbounds|FlowInfo::error_unimplemented|
		      FlowInfo::error_reinterpreted|FlowInfo::flow_forinline);
  inlineflow.forwardRecursion(flow);
  inlineflow.generateOps();

  int4 res;
  if (inlineflow.checkEZModel()) {
    res = 0;
    // With an EZ clone there are no jumptables to clone
    list<PcodeOp *>::const_iterator oiter = obank.endDead();
    --oiter;			// There is at least one op
    flow.inlineEZClone(inlineflow,callop->getAddr());
    ++oiter;
    if (oiter != obank.endDead()) { // If there was at least one PcodeOp cloned
      PcodeOp *firstop = *oiter;
      oiter = obank.endDead();
      --oiter;
      PcodeOp *lastop = *oiter;
      obank.moveSequenceDead(firstop,lastop,callop); // Move cloned sequence to right after callop
      if (callop->isBlockStart()) {
	firstop->setFlag(PcodeOp::startbasic); // First op of inline inherits callop's startbasic flag
	flow.updateTarget(callop, firstop);
      }
      else
	firstop->clearFlag(PcodeOp::startbasic);
    }
    opDestroyRaw(callop);
  }
  else {
    Address retaddr;
    if (!flow.testHardInlineRestrictions(inlinefd,callop,retaddr))
      return -1;
    res = 1;
    vector<JumpTable *>::const_iterator jiter; // Clone any jumptables from inline piece
    for(jiter=inlinefd->jumpvec.begin();jiter!=inlinefd->jumpvec.end();++jiter) {
      JumpTable *jtclone = new JumpTable(*jiter);
      jumpvec.push_back(jtclone);
    }
    flow.inlineClone(inlineflow,retaddr);

    // Convert CALL op to a jump
    while(callop->numInput()>1)
      opRemoveInput(callop,callop->numInput()-1);

    opSetOpcode(callop,CPUI_BRANCH);
    Varnode *inlineaddr = newCodeRef( inlinefd->getAddress() );
    opSetInput(callop,inlineaddr,0);
  }

  obank.setUniqId( inlinefd->obank.getUniqId() );
  
  return res;
}

/// \brief Find the primary branch operation for an instruction
///
/// For machine instructions that branch, this finds the \e primary PcodeOp that performs
/// the branch.  The instruction is provided as a list of p-code ops, and the caller can
/// specify whether they expect to see a \e branch, \e call, or \e return operation.
/// \param iter is the start of the operations for the instruction
/// \param enditer is the end of the operations for the instruction
/// \param findbranch is \b true if the caller expects to see a BRANCH, CBRANCH, or BRANCHIND
/// \param findcall is \b true if the caller expects to see CALL or CALLIND
/// \param findreturn is \b true if the caller expects to see RETURN
/// \return the first branching PcodeOp that matches the criteria or NULL
PcodeOp *Funcdata::findPrimaryBranch(PcodeOpTree::const_iterator iter,PcodeOpTree::const_iterator enditer,
				     bool findbranch,bool findcall,bool findreturn)
{
  while(iter != enditer) {
    PcodeOp *op = (*iter).second;
    switch(op->code()) {
    case CPUI_BRANCH:
    case CPUI_CBRANCH:
      if (findbranch) {
	if (!op->getIn(0)->isConstant()) // Make sure this is not an internal branch
	  return op;
      }
      break;
    case CPUI_BRANCHIND:
      if (findbranch)
	return op;
      break;
    case CPUI_CALL:
    case CPUI_CALLIND:
      if (findcall)
	return op;
      break;
    case CPUI_RETURN:
      if (findreturn)
	return op;
      break;
    default:
      break;
    }
    ++iter;
  }
  return (PcodeOp *)0;
}

/// \brief Override the control-flow p-code for a particular instruction
///
/// P-code in \b this function is modified to change the control-flow of
/// the instruction at the given address, based on the Override type.
/// \param addr is the given address of the instruction to modify
/// \param type is the Override type
void Funcdata::overrideFlow(const Address &addr,uint4 type)

{
  PcodeOpTree::const_iterator iter = beginOp(addr);
  PcodeOpTree::const_iterator enditer = endOp(addr);

  PcodeOp *op = (PcodeOp *)0;
  if (type == Override::BRANCH)
    op = findPrimaryBranch(iter,enditer,false,true,true);
  else if (type == Override::CALL)
    op = findPrimaryBranch(iter,enditer,true,false,true);
  else if (type == Override::CALL_RETURN)
    op = findPrimaryBranch(iter,enditer,true,true,true);
  else if (type == Override::RETURN)
    op = findPrimaryBranch(iter,enditer,true,true,false);

  if ((op == (PcodeOp *)0)||(!op->isDead()))
    throw LowlevelError("Could not apply flowoverride");

  OpCode opc = op->code();
  if (type == Override::BRANCH) {
    if (opc == CPUI_CALL)
      opSetOpcode(op,CPUI_BRANCH);
    else if (opc == CPUI_CALLIND)
      opSetOpcode(op,CPUI_BRANCHIND);
    else if (opc == CPUI_RETURN)
      opSetOpcode(op,CPUI_BRANCHIND);
  }
  else if ((type == Override::CALL)||(type == Override::CALL_RETURN)) {
    if (opc == CPUI_BRANCH)
      opSetOpcode(op,CPUI_CALL);
    else if (opc == CPUI_BRANCHIND)
      opSetOpcode(op,CPUI_CALLIND);
    else if (opc == CPUI_CBRANCH)
      throw LowlevelError("Do not currently support CBRANCH overrides");
    else if (opc == CPUI_RETURN)
      opSetOpcode(op,CPUI_CALLIND);
    if (type == Override::CALL_RETURN) { // Insert a new return op after call
      PcodeOp *newReturn = newOp(1,addr);
      opSetOpcode(newReturn,CPUI_RETURN);
      opSetInput(newReturn,newConstant(1,0),0);
      opDeadInsertAfter(newReturn,op);
    }
  }
  else if (type == Override::RETURN) {
    if ((opc == CPUI_BRANCH)||(opc == CPUI_CBRANCH)||(opc == CPUI_CALL))
      throw LowlevelError("Do not currently support complex overrides");
    else if (opc == CPUI_BRANCHIND)
      opSetOpcode(op,CPUI_RETURN);
    else if (opc == CPUI_CALLIND)
      opSetOpcode(op,CPUI_RETURN);
  }
}

/// Do in-place replacement of
///   - `c <= x`   with  `c-1 < x`   OR
///   - `x <= c`   with  `x < c+1`
///
/// \param op is comparison PcodeOp
/// \return true if a valid replacement was performed
bool Funcdata::replaceLessequal(PcodeOp *op)

{
  Varnode *vn;
  int4 i;
  intb val,diff;
  
  if ((vn=op->getIn(0))->isConstant()) {
    diff = -1;
    i = 0;
  }
  else if ((vn=op->getIn(1))->isConstant()) {
    diff = 1;
    i = 1;
  }
  else
    return false;

  val = sign_extend(vn->getOffset(),8*vn->getSize()-1);
  if (op->code() == CPUI_INT_SLESSEQUAL) {
    if ((val<0)&&(val+diff>0)) return false; // Check for sign overflow
    if ((val>0)&&(val+diff<0)) return false;
    opSetOpcode(op,CPUI_INT_SLESS);
  }
  else {			// Check for unsigned overflow
    if ((diff==-1)&&(val==0)) return false;
    if ((diff==1)&&(val==-1)) return false;
    opSetOpcode(op,CPUI_INT_LESS);
  }
  uintb res = (val+diff) & calc_mask(vn->getSize());
  Varnode *newvn = newConstant(vn->getSize(),res);
  newvn->copySymbol(vn);	// Preserve data-type (and any Symbol info)
  opSetInput(op,newvn,i);
  return true;
}

/// If a term has a multiplicative coefficient, but the underlying term is still additive,
/// in some situations we may need to distribute the coefficient before simplifying further.
/// The given PcodeOp is a INT_MULT where the second input is a constant. We also
/// know the first input is formed with INT_ADD. Distribute the coefficient to the INT_ADD inputs.
/// \param op is the given PcodeOp
/// \return \b true if the action was performed
bool Funcdata::distributeIntMultAdd(PcodeOp *op)

{
  Varnode *newvn0,*newvn1;
  PcodeOp *addop = op->getIn(0)->getDef();
  Varnode *vn0 = addop->getIn(0);
  Varnode *vn1 = addop->getIn(1);
  if ((vn0->isFree())&&(!vn0->isConstant())) return false;
  if ((vn1->isFree())&&(!vn1->isConstant())) return false;
  uintb coeff = op->getIn(1)->getOffset();
  int4 sz = op->getOut()->getSize();
				// Do distribution
  if (vn0->isConstant()) {
    uintb val = coeff * vn0->getOffset();
    val &= calc_mask(sz);
    newvn0 = newConstant(sz,val);
  }
  else {
    PcodeOp *newop0 = newOp(2,op->getAddr());
    opSetOpcode(newop0,CPUI_INT_MULT);
    newvn0 = newUniqueOut(sz,newop0);
    opSetInput(newop0, vn0, 0); // To first input of original add
    Varnode *newcvn = newConstant(sz,coeff);
    opSetInput(newop0, newcvn, 1);
    opInsertBefore(newop0, op);
  }

  if (vn1->isConstant()) {
    uintb val = coeff * vn1->getOffset();
    val &= calc_mask(sz);
    newvn1 = newConstant(sz,val);
  }
  else {
    PcodeOp *newop1 = newOp(2,op->getAddr());
    opSetOpcode(newop1,CPUI_INT_MULT);
    newvn1 = newUniqueOut(sz,newop1);
    opSetInput(newop1, vn1, 0); // To second input of original add
    Varnode *newcvn = newConstant(sz,coeff);
    opSetInput(newop1, newcvn, 1);
    opInsertBefore(newop1, op);
  }

  opSetInput( op, newvn0, 0); // new ADD's inputs are outputs of new MULTs
  opSetInput( op, newvn1, 1);
  opSetOpcode(op, CPUI_INT_ADD);

  return true;
}

/// If:
///   - The given Varnode is defined by a CPUI_INT_MULT.
///   - The second input to the INT_MULT is a constant.
///   - The first input is defined by another CPUI_INT_MULT,
///   - This multiply is also by a constant.
///
/// The constants are combined and \b true is returned.
/// Otherwise no change is made and \b false is returned.
/// \param vn is the given Varnode
/// \return \b true if a change was made
bool Funcdata::collapseIntMultMult(Varnode *vn)

{
  if (!vn->isWritten()) return false;
  PcodeOp *op = vn->getDef();
  if (op->code() != CPUI_INT_MULT) return false;
  Varnode *constVnFirst = op->getIn(1);
  if (!constVnFirst->isConstant()) return false;
  if (!op->getIn(0)->isWritten()) return false;
  PcodeOp *otherMultOp = op->getIn(0)->getDef();
  if (otherMultOp->code() != CPUI_INT_MULT) return false;
  Varnode *constVnSecond = otherMultOp->getIn(1);
  if (!constVnSecond->isConstant()) return false;
  Varnode *invn = otherMultOp->getIn(0);
  if (invn->isFree()) return false;
  int4 sz = invn->getSize();
  uintb val = (constVnFirst->getOffset() * constVnSecond->getOffset()) & calc_mask(sz);
  Varnode *newvn = newConstant(sz,val);
  opSetInput(op,newvn,1);
  opSetInput(op,invn,0);
  return true;
}

/// Return a Varnode in the \e unique space that is defined by a COPY op taking the given Varnode as input.
/// If a COPY op to a \e unique already exists, it may be returned. If the preexisting COPY is not usable
/// at the specified \b point, it is redefined at an earlier point in the control-flow so that it can be used.
/// \param vn is the given Varnode to COPY
/// \param point is the PcodeOp where the copy needs to be available
/// \return the \e unique Varnode COPY
Varnode *Funcdata::buildCopyTemp(Varnode *vn,PcodeOp *point)

{
  PcodeOp *otherOp = (PcodeOp *)0;
  PcodeOp *usedCopy = (PcodeOp *)0;
  list<PcodeOp *>::const_iterator iter;
  for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
    PcodeOp *op = *iter;
    if (op->code() != CPUI_COPY) continue;
    Varnode *outvn = op->getOut();
    if (outvn->getSpace()->getType() == IPTR_INTERNAL) {
      if (outvn->isTypeLock())
	continue;
      otherOp = op;
      break;
    }
  }
  if (otherOp != (PcodeOp *)0) {
    if (point->getParent() == otherOp->getParent()) {
      if (point->getSeqNum().getOrder() < otherOp->getSeqNum().getOrder())
	usedCopy = (PcodeOp *)0;
      else
	usedCopy = otherOp;
    }
    else {
      BlockBasic *common;
      common = (BlockBasic *)FlowBlock::findCommonBlock(point->getParent(),otherOp->getParent());
      if (common == point->getParent())
	usedCopy = (PcodeOp *)0;
      else if (common == otherOp->getParent())
	usedCopy = otherOp;
      else {			// Neither op is ancestor of the other
	usedCopy = newOp(1,common->getStop());
	opSetOpcode(usedCopy,CPUI_COPY);
	newUniqueOut(vn->getSize(),usedCopy);
	opSetInput(usedCopy,vn,0);
	opInsertEnd(usedCopy,common);
      }
    }
  }
  if (usedCopy == (PcodeOp *)0) {
    usedCopy = newOp(1,point->getAddr());
    opSetOpcode(usedCopy, CPUI_COPY);
    newUniqueOut(vn->getSize(), usedCopy);
    opSetInput(usedCopy, vn, 0);
    opInsertBefore(usedCopy, point);
  }
  if (otherOp != (PcodeOp *)0 && otherOp != usedCopy) {
    totalReplace(otherOp->getOut(),usedCopy->getOut());
    opDestroy(otherOp);
  }
  return usedCopy->getOut();
}

/// \brief Trace a boolean value to a set of PcodeOps that can be changed to flip the boolean value
///
/// The boolean Varnode is either the output of the given PcodeOp or the
/// first input if the PcodeOp is a CBRANCH. The list of ops that need flipping is
/// returned in an array
/// \param op is the given PcodeOp
/// \param fliplist is the array that will hold the ops to flip
/// \return 0 if the change normalizes, 1 if the change is ambivalent, 2 if the change does not normalize
int4 Funcdata::opFlipInPlaceTest(PcodeOp *op,vector<PcodeOp *> &fliplist)

{
  Varnode *vn;
  int4 subtest1,subtest2;
  switch(op->code()) {
  case CPUI_CBRANCH:
    vn = op->getIn(1);
    if (vn->loneDescend() != op) return 2;
    if (!vn->isWritten()) return 2;
    return opFlipInPlaceTest(vn->getDef(),fliplist);
  case CPUI_INT_EQUAL:
  case CPUI_FLOAT_EQUAL:
    fliplist.push_back(op);
    return 1;
  case CPUI_BOOL_NEGATE:
  case CPUI_INT_NOTEQUAL:
  case CPUI_FLOAT_NOTEQUAL:
    fliplist.push_back(op);
    return 0;
  case CPUI_INT_SLESS:
  case CPUI_INT_LESS:
    vn = op->getIn(0);
    fliplist.push_back(op);
    if (!vn->isConstant()) return 1;
    return 0;
  case CPUI_INT_SLESSEQUAL:
  case CPUI_INT_LESSEQUAL:
    vn = op->getIn(1);
    fliplist.push_back(op);
    if (vn->isConstant()) return 1;
    return 0;
  case CPUI_BOOL_OR:
  case CPUI_BOOL_AND:
    vn = op->getIn(0);
    if (vn->loneDescend() != op) return 2;
    if (!vn->isWritten()) return 2;
    subtest1 = opFlipInPlaceTest(vn->getDef(),fliplist);
    if (subtest1 == 2)
      return 2;
    vn = op->getIn(1);
    if (vn->loneDescend() != op) return 2;
    if (!vn->isWritten()) return 2;
    subtest2 = opFlipInPlaceTest(vn->getDef(),fliplist);
    if (subtest2 == 2)
      return 2;
    fliplist.push_back(op);
    return subtest1;		// Front of AND/OR must be normalizing
  default:
    break;
  }
  return 2;
}

/// \brief Perform op-code flips (in-place) to change a boolean value
///
/// The precomputed list of PcodeOps have their op-codes modified to
/// facilitate the flip.
/// \param fliplist is the list of PcodeOps to modify
void Funcdata::opFlipInPlaceExecute(vector<PcodeOp *> &fliplist)

{
  Varnode *vn;
  for(int4 i=0;i<fliplist.size();++i) {
    PcodeOp *op = fliplist[i];
    bool flipyes;
    OpCode opc = get_booleanflip(op->code(),flipyes);
    if (opc == CPUI_COPY) {	// We remove this (CPUI_BOOL_NEGATE) entirely
      vn = op->getIn(0);
      PcodeOp *otherop = op->getOut()->loneDescend(); // Must be a lone descendant
      int4 slot = otherop->getSlot(op->getOut());
      opSetInput(otherop,vn,slot);	// Propagate -vn- into otherop
      opDestroy(op);
    }
    else if (opc == CPUI_MAX) {
      if (op->code() == CPUI_BOOL_AND)
	opSetOpcode(op,CPUI_BOOL_OR);
      else if (op->code() == CPUI_BOOL_OR)
	opSetOpcode(op,CPUI_BOOL_AND);
      else
	throw LowlevelError("Bad flipInPlace op");
    }
    else {
      opSetOpcode(op,opc);
      if (flipyes) {
	opSwapInput(op,0,1);

	if ((opc == CPUI_INT_LESSEQUAL)||(opc == CPUI_INT_SLESSEQUAL))
	  replaceLessequal(op);
      }
    }
  }
}

/// \brief Find a duplicate calculation of a given PcodeOp reading a specific Varnode
///
/// We only match 1 level of calculation.  Additionally the duplicate must occur in the
/// indicated basic block, earlier than a specified op.
/// \param op is the given PcodeOp
/// \param vn is the specific Varnode that must be involved in the calculation
/// \param bl is the indicated basic block
/// \param earliest is the specified op to be earlier than
/// \return the discovered duplicate PcodeOp or NULL
PcodeOp *Funcdata::cseFindInBlock(PcodeOp *op,Varnode *vn,BlockBasic *bl,PcodeOp *earliest)

{
  list<PcodeOp *>::const_iterator iter;
  
  for(iter=vn->beginDescend();iter!=vn->endDescend();++iter) {
    PcodeOp *res = *iter;
    if (res == op) continue;	// Must not be -op-
    if (res->getParent() != bl) continue; // Must be in -bl-
    if (earliest != (PcodeOp *)0) {
      if (earliest->getSeqNum().getOrder() < res->getSeqNum().getOrder()) continue; // Must occur earlier than earliest
    }
    Varnode *outvn1 = op->getOut();
    Varnode *outvn2 = res->getOut();
    if (outvn2 == (Varnode *)0) continue;
    Varnode *buf1[2];
    Varnode *buf2[2];
    if (functionalEqualityLevel(outvn1,outvn2,buf1,buf2) == 0)
      return res;
  }
  return (PcodeOp *)0;
}

/// \brief Perform a Common Subexpression Elimination step
///
/// Assuming the two given PcodeOps perform the identical operation on identical operands
/// (depth 1 functional equivalence) eliminate the redundancy.  Return the remaining (dominating)
/// PcodeOp. If neither op dominates the other, both are eliminated, and a new PcodeOp
/// is built at a commonly accessible point.
/// \param op1 is the first of the given PcodeOps
/// \param op2 is the second given PcodeOp
/// \return the dominating PcodeOp
PcodeOp *Funcdata::cseElimination(PcodeOp *op1,PcodeOp *op2)

{
  PcodeOp *replace;

  if (op1->getParent() == op2->getParent()) {
    if (op1->getSeqNum().getOrder() < op2->getSeqNum().getOrder())
      replace = op1;
    else
      replace = op2;
  }
  else {
    BlockBasic *common;
    common = (BlockBasic *)FlowBlock::findCommonBlock(op1->getParent(),op2->getParent());
    if (common == op1->getParent())
      replace = op1;
    else if (common == op2->getParent())
      replace = op2;
    else {			// Neither op is ancestor of the other
      replace = newOp(op1->numInput(),common->getStop());
      opSetOpcode(replace,op1->code());
      newVarnodeOut(op1->getOut()->getSize(),op1->getOut()->getAddr(),replace);
      for(int4 i=0;i<op1->numInput();++i) {
	if (op1->getIn(i)->isConstant())
	  opSetInput(replace,newConstant(op1->getIn(i)->getSize(),op1->getIn(i)->getOffset()),i);
	else
	  opSetInput(replace,op1->getIn(i),i);
      }
      opInsertEnd(replace,common);
    }
  }
  if (replace != op1) {
    totalReplace(op1->getOut(),replace->getOut());
    opDestroy(op1);
  }
  if (replace != op2) {
    totalReplace(op2->getOut(),replace->getOut());
    opDestroy(op2);
  }
  return replace;
}

/// \brief Comparator for (hash,PcodeOp) pairs
///
/// Compare by hash.
/// \param a is the first pair
/// \param b is the second pair
/// \return \b true if the first comes before the second
static bool compareCseHash(const pair<uintm,PcodeOp *> &a,const pair<uintm,PcodeOp *> &b)

{
  return (a.first < b.first);
}

/// \brief Perform Common Subexpression Elimination on a list of Varnode descendants
///
/// The list consists of PcodeOp descendants of a single Varnode paired with a hash value.
/// The hash serves as a primary test for duplicate calculations; if it doesn't match
/// the PcodeOps aren't common subexpressions.  This method searches for hash matches
/// then does secondary testing and eliminates any redundancy it finds.
/// \param list is the list of (hash, PcodeOp) pairs
/// \param outlist will hold Varnodes produced by duplicate calculations
void Funcdata::cseEliminateList(vector< pair<uintm,PcodeOp *> > &list,vector<Varnode *> &outlist)

{
  PcodeOp *op1,*op2,*resop;
  vector< pair<uintm,PcodeOp *> >::iterator liter1,liter2;

  if (list.empty()) return;
  stable_sort(list.begin(),list.end(),compareCseHash);
  liter1 = list.begin();
  liter2 = list.begin();
  liter2++;
  while(liter2 != list.end()) {
    if ((*liter1).first == (*liter2).first) {
      op1 = (*liter1).second;
      op2 = (*liter2).second;
      if ((!op1->isDead())&&(!op2->isDead())&&op1->isCseMatch(op2)) {
	Varnode *outvn1 = op1->getOut();
	Varnode *outvn2 = op2->getOut();
	if ((outvn1 == (Varnode *)0)||isHeritaged(outvn1)) {
	  if ((outvn2 == (Varnode *)0)||isHeritaged(outvn2)) {
	    resop = cseElimination(op1,op2);
	    outlist.push_back(resop->getOut());
	  }
	}
      }
    }
    liter1++;
    liter2++;
  }
}

/// This routine should be called only after Varnode merging and explicit/implicit attributes have
/// been calculated.  Determine if the given op can be moved (only within its basic block) to
/// after \e lastOp.  The output of any PcodeOp moved across must not be involved, directly or
/// indirectly, with any variable in the expression rooted at the given op.
/// If the move is possible, perform the move.
/// \param op is the given PcodeOp
/// \param lastOp is the PcodeOp to move past
/// \return \b true if the move is possible
bool Funcdata::moveRespectingCover(PcodeOp *op,PcodeOp *lastOp)

{
  if (op == lastOp) return true;	// Nothing to move past
  if (op->isCall()) return false;
  PcodeOp *prevOp = (PcodeOp *)0;
  if (op->code() == CPUI_CAST) {
    Varnode *vn = op->getIn(0);
    if (!vn->isExplicit()) {		// If CAST is part of expression, we need to move the previous op as well
      if (!vn->isWritten()) return false;
      prevOp = vn->getDef();
      if (prevOp->isCall()) return false;
      if (op->previousOp() != prevOp) return false;	// Previous op must exist and feed into the CAST
    }
  }
  Varnode *rootvn = op->getOut();
  vector<HighVariable *> highList;
  int4 typeVal = HighVariable::markExpression(rootvn, highList);
  PcodeOp *curOp = op;
  do {
    PcodeOp *nextOp = curOp->nextOp();
    OpCode opc = nextOp->code();
    if (opc != CPUI_COPY && opc != CPUI_CAST) break;	// Limit ourselves to only crossing COPY and CAST ops
    if (rootvn == nextOp->getIn(0)) break;	// Data-flow order dependence
    Varnode *copyVn = nextOp->getOut();
    if (copyVn->getHigh()->isMark()) break;	// Direct interference: COPY writes what original op reads
    if (typeVal != 0 && copyVn->isAddrTied()) break;	// Possible indirect interference
    curOp = nextOp;
  } while(curOp != lastOp);
  for(int4 i=0;i<highList.size();++i)		// Clear marks on expression
    highList[i]->clearMark();
  if (curOp == lastOp) {			// If we are able to cross everything
    opUninsert(op);				// Move -op-
    opInsertAfter(op, lastOp);
    if (prevOp != (PcodeOp *)0) {		// If there was a CAST, move both ops
      opUninsert(prevOp);
      opInsertAfter(prevOp, lastOp);
    }
    return true;
  }
  return false;
}

} // End namespace ghidra
