/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.compilers.opt.lir2mir.ia32;

import org.jikesrvm.VM;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.compilers.opt.DefUse;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.ir.Binary;
import org.jikesrvm.compilers.opt.ir.CacheOp;
import org.jikesrvm.compilers.opt.ir.Call;
import org.jikesrvm.compilers.opt.ir.CondMove;
import org.jikesrvm.compilers.opt.ir.GuardedBinary;
import org.jikesrvm.compilers.opt.ir.IfCmp;
import org.jikesrvm.compilers.opt.ir.LowTableSwitch;
import org.jikesrvm.compilers.opt.ir.MIR_BinaryAcc;
import org.jikesrvm.compilers.opt.ir.MIR_Call;
import org.jikesrvm.compilers.opt.ir.MIR_Compare;
import org.jikesrvm.compilers.opt.ir.MIR_CompareExchange;
import org.jikesrvm.compilers.opt.ir.MIR_CompareExchange8B;
import org.jikesrvm.compilers.opt.ir.MIR_CondBranch;
import org.jikesrvm.compilers.opt.ir.MIR_CondMove;
import org.jikesrvm.compilers.opt.ir.MIR_ConvertDW2QW;
import org.jikesrvm.compilers.opt.ir.MIR_Divide;
import org.jikesrvm.compilers.opt.ir.MIR_Lea;
import org.jikesrvm.compilers.opt.ir.MIR_LowTableSwitch;
import org.jikesrvm.compilers.opt.ir.MIR_Move;
import org.jikesrvm.compilers.opt.ir.MIR_Multiply;
import org.jikesrvm.compilers.opt.ir.MIR_Nullary;
import org.jikesrvm.compilers.opt.ir.MIR_RDTSC;
import org.jikesrvm.compilers.opt.ir.MIR_Set;
import org.jikesrvm.compilers.opt.ir.MIR_TrapIf;
import org.jikesrvm.compilers.opt.ir.MIR_Unary;
import org.jikesrvm.compilers.opt.ir.MIR_UnaryAcc;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.Nullary;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.Operator;
import static org.jikesrvm.compilers.opt.ir.Operators.CALL_SAVE_VOLATILE;
import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_CMPL;
import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_CMPL;
import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_ADC;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_ADD;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_AND;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_CALL;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_CDQ;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_CMOV;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_CMP;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FCMOV;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FCOMI;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FCOMIP;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FFREE;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FILD;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FIST;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FLD;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FLD1;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FLDL2E;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FLDL2T;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FLDLG2;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FLDLN2;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FLDPI;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FLDZ;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FMOV;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FPREM;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_FSTP;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_IDIV;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_IMUL2;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_JCC;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_LEA;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_LOCK_CMPXCHG;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_LOCK_CMPXCHG8B;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_MOV;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_MOVD;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_MOVLPD;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_MOVSD;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_MOVSS;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_MOVSX__B;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_MOVZX__B;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_MUL;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_NEG;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_NOT;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_OR;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_RCR;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_RDTSC;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_SAR;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_SBB;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_SET__B;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_SHL;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_SHR;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_SUB;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_SYSCALL;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_TRAPIF;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_XOR;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_XORPD;
import static org.jikesrvm.compilers.opt.ir.Operators.IA32_XORPS;
import static org.jikesrvm.compilers.opt.ir.Operators.IR_PROLOGUE;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_SHL;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_SHR;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_USHR;
import static org.jikesrvm.compilers.opt.ir.Operators.MIR_LOWTABLESWITCH;
import org.jikesrvm.compilers.opt.ir.Register;
import org.jikesrvm.compilers.opt.ir.RegisterOperandEnumeration;
import org.jikesrvm.compilers.opt.ir.OsrPoint;
import org.jikesrvm.compilers.opt.ir.Prologue;
import org.jikesrvm.compilers.opt.ir.TrapIf;
import org.jikesrvm.compilers.opt.ir.Unary;
import org.jikesrvm.compilers.opt.ir.operand.BranchOperand;
import org.jikesrvm.compilers.opt.ir.operand.BranchProfileOperand;
import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand;
import org.jikesrvm.compilers.opt.ir.operand.ConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.DoubleConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.FloatConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.InlinedOsrTypeInfoOperand;
import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.LocationOperand;
import org.jikesrvm.compilers.opt.ir.operand.LongConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.MemoryOperand;
import org.jikesrvm.compilers.opt.ir.operand.MethodOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
import org.jikesrvm.compilers.opt.ir.operand.StackLocationOperand;
import org.jikesrvm.compilers.opt.ir.operand.TrapCodeOperand;
import org.jikesrvm.compilers.opt.ir.operand.TrueGuardOperand;
import org.jikesrvm.compilers.opt.ir.operand.ia32.BURSManagedFPROperand;
import org.jikesrvm.compilers.opt.ir.operand.ia32.IA32ConditionOperand;
import org.jikesrvm.compilers.opt.lir2mir.BURS;
import org.jikesrvm.compilers.opt.lir2mir.BURS_MemOp_Helpers;
import org.jikesrvm.runtime.Entrypoints;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.runtime.RuntimeEntrypoints;
import org.vmmagic.unboxed.Offset;

/**
 * Contains IA32-specific helper functions for BURS.
 */
abstract class BURS_Helpers extends BURS_MemOp_Helpers {
  /** Constant log10(2), supported as an x87 constant */
  private static final double LG2 = Double
      .parseDouble("0.3010299956639811952256464283594894482");

  /** Constant ln(2), supported as an x87 constant */
  private static final double LN2 = Double
      .parseDouble("0.6931471805599453094286904741849753009");

  /** Constant log2(e), supported as an x87 constant */
  private static final double L2E = Double
      .parseDouble("1.4426950408889634073876517827983434472");

  /** Constant log2(10), supported as an x87 constant */
  private static final double L2T = Double
      .parseDouble("3.3219280948873623478083405569094566090");

  /**
   * When emitting certain rules this holds the condition code state to be
   * consumed by a parent rule
   */
  private ConditionOperand cc;

  /** Constructor */
  BURS_Helpers(BURS burs) {
    super(burs);
  }

  /**
   * Create the MIR instruction given by operator from the Binary LIR operands
   * @param operator the MIR operator
   * @param s the instruction being replaced
   * @param result the destination register/memory
   * @param val1 the first operand
   * @param val2 the second operand
   */
  protected void EMIT_Commutative(Operator operator, Instruction s, Operand result, Operand val1, Operand val2) {
    if(VM.VerifyAssertions) VM._assert(result.isRegister() || result.isMemory());
    // Swap operands to reduce chance of generating a move or to normalize
    // constants into val2
    if (val2.similar(result) || val1.isConstant()) {
      Operand temp = val1;
      val1 = val2;
      val2 = temp;
    }
    // Do we need to move prior to the operator - result = val1
    if (!result.similar(val1)) {
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, result.copy(), val1)));
    }
    EMIT(MIR_BinaryAcc.mutate(s, operator, result, val2));
  }

  /**
   * Create the MIR instruction given by operator from the Binary LIR operands
   * @param operator the MIR operator
   * @param s the instruction being replaced
   * @param result the destination register/memory
   * @param val1 the first operand
   * @param val2 the second operand
   */
  protected void EMIT_NonCommutative(Operator operator, Instruction s, Operand result, Operand val1, Operand val2) {
    if(VM.VerifyAssertions) VM._assert(result.isRegister() || result.isMemory());
    if (result.similar(val1)) {
      // Straight forward case where instruction is already in accumulate form
      EMIT(MIR_BinaryAcc.mutate(s, operator, result, val2));
    } else if (!result.similar(val2)) {
      // Move first operand to result and perform operator on result, if
      // possible redundant moves should be remove by register allocator
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, result.copy(), val1)));
      EMIT(MIR_BinaryAcc.mutate(s, operator, result, val2));
    } else {
      // Potential to clobber second operand during move to result. Use a
      // temporary register to perform the operation and rely on register
      // allocator to remove redundant moves
      RegisterOperand temp = regpool.makeTemp(result);
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, temp, val1)));
      EMIT(MIR_BinaryAcc.mutate(s, operator, temp.copyRO(), val2));
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, result, temp.copyRO())));
    }
  }

  /**
   * Create the MIR instruction given by operator from the Binary LIR operands
   * @param operator the MIR operator
   * @param s the instruction being replaced
   * @param result the destination register/memory
   * @param value the first operand
   */
  protected void EMIT_Unary(Operator operator, Instruction s, Operand result, Operand value) {
    if(VM.VerifyAssertions) VM._assert(result.isRegister() || result.isMemory());
    // Do we need to move prior to the operator - result = val1
    if (!result.similar(value)) {
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, result.copy(), value)));
    }
    EMIT(MIR_UnaryAcc.mutate(s, operator, result));
  }

  /**
   * Create the MIR LEA instruction performing a few simplifications if possible
   * @param s the instruction being replaced
   * @param result the destination register
   * @param mo the memory operand
   */
  protected void EMIT_Lea(Instruction s, RegisterOperand result, MemoryOperand mo) {
    // A memory operand is: base + scaled index + displacement
    if ((mo.index == null) && mo.disp.isZero()) {
      if (VM.VerifyAssertions) VM._assert(mo.scale == 0 && mo.base != null);
      // If there is no index or displacement emit a move
      EMIT(MIR_Move.mutate(s, IA32_MOV, result, mo.base));
    } else if ((mo.index == null) && result.similar(mo.base)) {
      if (VM.VerifyAssertions) VM._assert(mo.scale == 0);
      // If there is no index and we're redefining the same register, emit an add
      EMIT(MIR_BinaryAcc.mutate(s, IA32_ADD, result, IC(mo.disp.toInt())));
    } else {
      // Lea is simplest form
      EMIT(MIR_Lea.mutate(s, IA32_LEA, result, mo));
    }
  }

  /**
   * Convert the given comparison with a boolean (int) value into a condition
   * suitable for the carry flag
   * @param x the value 1 (true) or 0 (false)
   * @param cond either equal or not equal
   * @return lower or higher equal
   */
  protected static ConditionOperand BIT_TEST(int x, ConditionOperand cond) {
    if (VM.VerifyAssertions) VM._assert((x==0)||(x==1));
    if (VM.VerifyAssertions) VM._assert(EQ_NE(cond));
    if ((x == 1 && cond.isEQUAL())||
        (x == 0 && cond.isNOT_EQUAL())) {
      return ConditionOperand.LOWER();
    } else {
      return ConditionOperand.HIGHER_EQUAL();
    }
  }

  /**
   * Follow a chain of Move operations filtering back to a def
   *
   * @param use the place to start from
   * @return the operand at the start of the chain
   */
  protected static Operand follow(Operand use) {
    if (!use.isRegister()) {
      return use;
    } else {
      RegisterOperand rop = use.asRegister();
      RegisterOperandEnumeration defs = DefUse.defs(rop.getRegister());
      if (!defs.hasMoreElements()) {
        return use;
      } else {
        Operand def = defs.next();
        if (defs.hasMoreElements()) {
          return def;
        } else {
          Instruction instr = def.instruction;
          if (Move.conforms(instr)) {
            return follow(Move.getVal(instr));
          } else if (MIR_Move.conforms(instr)) {
            return follow(MIR_Move.getValue(instr));
          } else {
            return def;
          }
        }
      }
    }
  }

  /**
   * Remember a condition code in a child node
   *
   * @param c condition code to record
   */
  protected final void pushCOND(ConditionOperand c) {
    if (VM.VerifyAssertions) {
      VM._assert(cc == null);
    }
    cc = c;
  }

  /**
   * Acquire remembered condition code in parent
   *
   * @return condition code
   */
  protected final ConditionOperand consumeCOND() {
    ConditionOperand ans = cc;
    if (VM.VerifyAssertions) {
      VM._assert(cc != null);
    }
    cc = null;
    return ans;
  }

  /**
   * Can an IV be the scale in a LEA instruction?
   *
   * @param op operand to examine
   * @param trueCost the cost if this can be part of an LEA
   * @return trueCost or INFINITE
   */
  protected final int LEA_SHIFT(Operand op, int trueCost) {
    return LEA_SHIFT(op, trueCost, INFINITE);
  }

  /**
   * Can an IV be the scale in a LEA instruction?
   *
   * @param op operand to examine
   * @param trueCost the cost if this can be part of an LEA
   * @param falseCost the cost if this can't be part of an LEA
   * @return trueCost or falseCost
   */
  protected final int LEA_SHIFT(Operand op, int trueCost, int falseCost) {
    if (op.isIntConstant()) {
      int val = IV(op);
      if (val >= 0 && val <= 3) {
        return trueCost;
      }
    }
    return falseCost;
  }

  protected final byte LEA_SHIFT(Operand op) {
    switch (IV(op)) {
      case 0:
        return B_S;
      case 1:
        return W_S;
      case 2:
        return DW_S;
      case 3:
        return QW_S;
      default:
        throw new OptimizingCompilerException("bad val for LEA shift " + op);
    }
  }

  /**
   * Is the given instruction's constant operand a x87 floating point constant
   *
   * @param s the instruction to examine
   * @param trueCost the cost if this is a valid constant
   * @return trueCost or INFINITE depending on the given constant
   */
  protected final int is387_FPC(Instruction s, int trueCost) {
    Operand val = Binary.getVal2(s);
    if (val instanceof FloatConstantOperand) {
      FloatConstantOperand fc = (FloatConstantOperand) val;
      if (fc.value == 1.0f) {
        return trueCost;
      } else if (fc.value == 0.0f) {
        return trueCost;
      } else if (fc.value == (float) Math.PI) {
        return trueCost;
      } else if (fc.value == (float) LG2) {
        return trueCost;
      } else if (fc.value == (float) LN2) {
        return trueCost;
      } else if (fc.value == (float) L2E) {
        return trueCost;
      } else if (fc.value == (float) L2T) {
        return trueCost;
      }
    } else {
      DoubleConstantOperand dc = (DoubleConstantOperand) val;
      if (dc.value == 1.0) {
        return trueCost;
      } else if (dc.value == 0.0) {
        return trueCost;
      } else if (dc.value == Math.PI) {
        return trueCost;
      } else if (dc.value == LG2) {
        return trueCost;
      } else if (dc.value == LN2) {
        return trueCost;
      } else if (dc.value == L2E) {
        return trueCost;
      } else if (dc.value == L2T) {
        return trueCost;
      }
    }
    return INFINITE;
  }

  protected final Operator get387_FPC(Instruction s) {
    Operand val = Binary.getVal2(s);
    if (val instanceof FloatConstantOperand) {
      FloatConstantOperand fc = (FloatConstantOperand) val;
      if (fc.value == 1.0f) {
        return IA32_FLD1;
      } else if (fc.value == 0.0f) {
        return IA32_FLDZ;
      } else if (fc.value == (float) Math.PI) {
        return IA32_FLDPI;
      } else if (fc.value == (float) LG2) {
        return IA32_FLDLG2;
      } else if (fc.value == (float) LN2) {
        return IA32_FLDLN2;
      } else if (fc.value == (float) L2E) {
        return IA32_FLDL2E;
      } else if (fc.value == (float) L2T) {
        return IA32_FLDL2T;
      }
    } else {
      DoubleConstantOperand dc = (DoubleConstantOperand) val;
      if (dc.value == 1.0) {
        return IA32_FLD1;
      } else if (dc.value == 0.0) {
        return IA32_FLDZ;
      } else if (dc.value == Math.PI) {
        return IA32_FLDPI;
      } else if (dc.value == LG2) {
        return IA32_FLDLG2;
      } else if (dc.value == LN2) {
        return IA32_FLDLN2;
      } else if (dc.value == L2E) {
        return IA32_FLDL2E;
      } else if (dc.value == L2T) {
        return IA32_FLDL2T;
      }
    }
    throw new OptimizingCompilerException("BURS_Helpers", "unexpected 387 constant " + val);
  }

  protected final IA32ConditionOperand COND(ConditionOperand op) {
    return new IA32ConditionOperand(op);
  }

  // Get particular physical registers
  protected final Register getEAX() {
    return getIR().regpool.getPhysicalRegisterSet().getEAX();
  }

  protected final Register getECX() {
    return getIR().regpool.getPhysicalRegisterSet().getECX();
  }

  protected final Register getEDX() {
    return getIR().regpool.getPhysicalRegisterSet().getEDX();
  }

  protected final Register getEBX() {
    return getIR().regpool.getPhysicalRegisterSet().getEBX();
  }

  protected final Register getESP() {
    return getIR().regpool.getPhysicalRegisterSet().getESP();
  }

  protected final Register getEBP() {
    return getIR().regpool.getPhysicalRegisterSet().getEBP();
  }

  protected final Register getESI() {
    return getIR().regpool.getPhysicalRegisterSet().getESI();
  }

  protected final Register getEDI() {
    return getIR().regpool.getPhysicalRegisterSet().getEDI();
  }

  protected final Register getFPR(int n) {
    return getIR().regpool.getPhysicalRegisterSet().getFPR(n);
  }

  protected final Operand myFP0() {
    return new BURSManagedFPROperand(0);
  }

  protected final Operand myFP1() {
    return new BURSManagedFPROperand(1);
  }

  protected final Register getST0() {
    return getIR().regpool.getPhysicalRegisterSet().getST0();
  }

  /**
   * Move op into a register operand if it isn't one already.
   */
  private Operand asReg(Instruction s, Operator movop, Operand op) {
    if (op.isRegister()) {
      return op;
    }
    RegisterOperand tmp = regpool.makeTemp(op);
    EMIT(CPOS(s, MIR_Move.create(movop, tmp, op)));
    return tmp.copy();
  }

  /**
   * Set the size field of the given memory operand and return it
   *
   * @param mo memory operand size to set
   * @param size the new size
   * @return mo
   */
  protected final MemoryOperand setSize(MemoryOperand mo, int size) {
    mo.size = (byte) size;
    return mo;
  }

  /**
   * Create a slot on the stack in memory for a conversion
   *
   * @param size for memory operand
   * @return memory operand of slot in stack
   */
  protected final Operand MO_CONV(byte size) {
    int offset = -burs.ir.stackManager.allocateSpaceForConversion();
    return new StackLocationOperand(true, offset, size);
  }

  /**
   * Create a 64bit slot on the stack in memory for a conversion and store the
   * given long
   */
  protected final void STORE_LONG_FOR_CONV(Operand op) {
    int offset = -burs.ir.stackManager.allocateSpaceForConversion();
    if (op instanceof RegisterOperand) {
      RegisterOperand hval = (RegisterOperand) op;
      RegisterOperand lval = new RegisterOperand(regpool.getSecondReg(hval.getRegister()),
          TypeReference.Int);
      EMIT(MIR_Move.create(IA32_MOV, new StackLocationOperand(true, offset + 4, DW), hval));
      EMIT(MIR_Move.create(IA32_MOV, new StackLocationOperand(true, offset, DW), lval));
    } else {
      LongConstantOperand val = LC(op);
      EMIT(MIR_Move.create(IA32_MOV, new StackLocationOperand(true, offset + 4, DW), IC(val.upper32())));
      EMIT(MIR_Move.create(IA32_MOV, new StackLocationOperand(true, offset, DW), IC(val.lower32())));
    }
  }

  /**
   * Create memory operand to load from a given jtoc offset
   *
   * @param offset location in JTOC
   * @param size of value in JTOC
   * @return created memory operand
   */
  static MemoryOperand loadFromJTOC(Offset offset, byte size) {
    LocationOperand loc = new LocationOperand(offset);
    Operand guard = TG();
    return MemoryOperand.D(Magic.getTocPointer().plus(offset), size, loc, guard);
  }

  /*
   * IA32-specific emit rules that are complex enough that we didn't want to
   * write them in the LIR2MIR.rules file. However, all expansions in this file
   * are called during BURS and thus are constrained to generate nonbranching
   * code (ie they can't create new basic blocks and/or do branching).
   */

  /**
   * Emit code to get a caught exception object into a register
   *
   * @param s the instruction to expand
   */
  protected final void GET_EXCEPTION_OBJECT(Instruction s) {
    int offset = -burs.ir.stackManager.allocateSpaceForCaughtException();
    StackLocationOperand sl = new StackLocationOperand(true, offset, DW);
    EMIT(MIR_Move.mutate(s, IA32_MOV, Nullary.getResult(s), sl));
  }

  /**
   * Emit code to move a value in a register to the stack location where a
   * caught exception object is expected to be.
   *
   * @param s the instruction to expand
   */
  protected final void SET_EXCEPTION_OBJECT(Instruction s) {
    int offset = -burs.ir.stackManager.allocateSpaceForCaughtException();
    StackLocationOperand sl = new StackLocationOperand(true, offset, DW);
    RegisterOperand obj = (RegisterOperand) CacheOp.getRef(s);
    EMIT(MIR_Move.mutate(s, IA32_MOV, sl, obj));
  }

  /**
   * Expansion of INT_2LONG
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param value the second operand
   * @param signExtend should the value be sign or zero extended?
   */
  protected final void INT_2LONG(Instruction s, RegisterOperand result,
Operand value, boolean signExtend) {
    Register hr = result.getRegister();
    Register lr = regpool.getSecondReg(hr);
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(lr, TypeReference.Int), value)));
    if (signExtend) {
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
          new RegisterOperand(hr, TypeReference.Int),
          new RegisterOperand(lr, TypeReference.Int))));
      EMIT(MIR_BinaryAcc.mutate(s,IA32_SAR,
          new RegisterOperand(hr, TypeReference.Int),
          IC(31)));
    } else {
      EMIT(MIR_Move.mutate(s, IA32_MOV,
          new RegisterOperand(hr, TypeReference.Int),
          IC(0)));
    }
  }

  /**
   * Expansion of FLOAT_2INT and DOUBLE_2INT, using the FIST instruction. This
   * expansion does some boolean logic and conditional moves in order to avoid
   * changing the floating-point rounding mode or inserting branches. Other
   * expansions are possible, and may be better?
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param value the second operand
   */
  protected final void FPR_2INT(Instruction s, RegisterOperand result, Operand value) {
    MemoryOperand M;

    // Step 1: Get value to be converted into myFP0
    // and in 'strict' IEEE mode.
    if (value instanceof MemoryOperand) {
      // value is in memory, all we have to do is load it
      EMIT(CPOS(s, MIR_Move.create(IA32_FLD, myFP0(), value)));
    } else {
      // sigh. value is an FP register. Unfortunately,
      // SPECjbb requires some 'strict' FP semantics. Naturally, we don't
      // normally implement strict semantics, but we try to slide by in
      // order to pass the benchmark.
      // In order to pass SPECjbb, it turns out we need to enforce 'strict'
      // semantics before doing a particular f2int conversion. To do this
      // we must have a store/load sequence to cause IEEE rounding.
      if (value instanceof BURSManagedFPROperand) {
        if (VM.VerifyAssertions) {
          VM._assert(value.similar(myFP0()));
        }
        EMIT(CPOS(s, MIR_Move.create(IA32_FSTP, MO_CONV(DW), value)));
        EMIT(CPOS(s, MIR_Move.create(IA32_FLD, myFP0(), MO_CONV(DW))));
      } else {
        EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, MO_CONV(DW), value)));
        EMIT(CPOS(s, MIR_Move.create(IA32_FLD, myFP0(), MO_CONV(DW))));
      }
    }

    // FP Stack: myFP0 = value
    EMIT(CPOS(s, MIR_Move.create(IA32_FIST, MO_CONV(DW), myFP0())));
    // MO_CONV now holds myFP0 converted to an integer (round-toward nearest)
    // FP Stack: myFP0 == value

    // isPositive == 1 iff 0.0 < value
    // isNegative == 1 iff 0.0 > value
    Register one = regpool.getInteger();
    Register isPositive = regpool.getInteger();
    Register isNegative = regpool.getInteger();
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(one, TypeReference.Int), IC(1))));
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(isPositive, TypeReference.Int), IC(0))));
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(isNegative, TypeReference.Int), IC(0))));
    EMIT(CPOS(s, MIR_Nullary.create(IA32_FLDZ, myFP0())));
    // FP Stack: myFP0 = 0.0; myFP1 = value
    EMIT(CPOS(s, MIR_Compare.create(IA32_FCOMIP, myFP0(), myFP1())));
    // FP Stack: myFP0 = value
    EMIT(CPOS(s, MIR_CondMove.create(IA32_CMOV,
                             new RegisterOperand(isPositive, TypeReference.Int),
                             new RegisterOperand(one, TypeReference.Int),
                             IA32ConditionOperand.LLT())));
    EMIT(CPOS(s, MIR_CondMove.create(IA32_CMOV,
                             new RegisterOperand(isNegative, TypeReference.Int),
                             new RegisterOperand(one, TypeReference.Int),
                             IA32ConditionOperand.LGT())));

    EMIT(CPOS(s, MIR_Move.create(IA32_FILD, myFP0(), MO_CONV(DW))));
    // FP Stack: myFP0 = round(value), myFP1 = value

    // addee = 1 iff round(x) < x
    // subtractee = 1 iff round(x) > x
    Register addee = regpool.getInteger();
    Register subtractee = regpool.getInteger();
    EMIT(CPOS(s, MIR_Compare.create(IA32_FCOMIP, myFP0(), myFP1())));
    // FP Stack: myFP0 = value
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(addee, TypeReference.Int), IC(0))));
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(subtractee, TypeReference.Int), IC(0))));
    EMIT(CPOS(s, MIR_CondMove.create(IA32_CMOV,
                             new RegisterOperand(addee, TypeReference.Int),
                             new RegisterOperand(one, TypeReference.Int),
                             IA32ConditionOperand.LLT())));
    EMIT(CPOS(s, MIR_CondMove.create(IA32_CMOV,
                             new RegisterOperand(subtractee, TypeReference.Int),
                             new RegisterOperand(one, TypeReference.Int),
                             IA32ConditionOperand.LGT())));

    // Now a little tricky part.
    // We will add 1 iff isNegative and x > round(x)
    // We will subtract 1 iff isPositive and x < round(x)
    EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_AND,
                              new RegisterOperand(addee, TypeReference.Int),
                              new RegisterOperand(isNegative, TypeReference.Int))));
    EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_AND,
                              new RegisterOperand(subtractee, TypeReference.Int),
                              new RegisterOperand(isPositive, TypeReference.Int))));
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, result.copy(), MO_CONV(DW))));
    EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD, result.copy(), new RegisterOperand(addee, TypeReference.Int))));
    EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB, result.copy(), new RegisterOperand(subtractee, TypeReference.Int))));

    // Compare myFP0 with (double)Integer.MAX_VALUE
    M = MemoryOperand.D(Magic.getTocPointer().plus(Entrypoints.maxintField.getOffset()), QW, null, null);
    EMIT(CPOS(s, MIR_Move.create(IA32_FLD, myFP0(), M)));
    // FP Stack: myFP0 = (double)Integer.MAX_VALUE; myFP1 = value
    EMIT(CPOS(s, MIR_Compare.create(IA32_FCOMIP, myFP0(), myFP1())));
    // FP Stack: myFP0 = value
    // If MAX_VALUE < value, then result := MAX_INT
    Register maxInt = regpool.getInteger();
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(maxInt, TypeReference.Int), IC(Integer.MAX_VALUE))));
    EMIT(CPOS(s, MIR_CondMove.create(IA32_CMOV,
                             result.copy(),
                             new RegisterOperand(maxInt, TypeReference.Int),
                             IA32ConditionOperand.LLT())));

    // Compare myFP0 with (double)Integer.MIN_VALUE
    M = MemoryOperand.D(Magic.getTocPointer().plus(Entrypoints.minintField.getOffset()), QW, null, null);
    EMIT(CPOS(s, MIR_Move.create(IA32_FLD, myFP0(), M)));
    // FP Stack: myFP0 = (double)Integer.MIN_VALUE; myFP1 = value
    EMIT(CPOS(s, MIR_Compare.create(IA32_FCOMIP, myFP0(), myFP1())));
    // FP Stack: myFP0 = value
    // If MIN_VALUE > value, then result := MIN_INT
    Register minInt = regpool.getInteger();
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(minInt, TypeReference.Int), IC(Integer.MIN_VALUE))));
    EMIT(CPOS(s, MIR_CondMove.create(IA32_CMOV,
                             result.copy(),
                             new RegisterOperand(minInt, TypeReference.Int),
                             IA32ConditionOperand.LGT())));

    // Set condition flags: set PE iff myFP0 is a NaN
    EMIT(CPOS(s, MIR_Compare.create(IA32_FCOMIP, myFP0(), myFP0())));
    // FP Stack: back to original level (all BURS managed slots freed)
    // If FP0 was classified as a NaN, then result := 0
    Register zero = regpool.getInteger();
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(zero, TypeReference.Int), IC(0))));
    EMIT(CPOS(s, MIR_CondMove.create(IA32_CMOV,
                             result.copy(),
                             new RegisterOperand(zero, TypeReference.Int),
                             IA32ConditionOperand.PE())));
  }

  /**
   * Emit code to move 64 bits from FPRs to GPRs
   */
  protected final void FPR2GPR_64(Instruction s) {
    int offset = -burs.ir.stackManager.allocateSpaceForConversion();
    StackLocationOperand sl = new StackLocationOperand(true, offset, QW);
    StackLocationOperand sl1 = new StackLocationOperand(true, offset + 4, DW);
    StackLocationOperand sl2 = new StackLocationOperand(true, offset, DW);
    EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, sl, Unary.getVal(s))));
    RegisterOperand i1 = Unary.getResult(s);
    RegisterOperand i2 = new RegisterOperand(regpool
        .getSecondReg(i1.getRegister()), TypeReference.Int);
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, i1, sl1)));
    EMIT(MIR_Move.mutate(s, IA32_MOV, i2, sl2));
  }

  /**
   * Emit code to move 64 bits from GPRs to FPRs
   */
  protected final void GPR2FPR_64(Instruction s) {
    int offset = -burs.ir.stackManager.allocateSpaceForConversion();
    StackLocationOperand sl = new StackLocationOperand(true, offset, QW);
    StackLocationOperand sl1 = new StackLocationOperand(true, offset + 4, DW);
    StackLocationOperand sl2 = new StackLocationOperand(true, offset, DW);
    Operand i1, i2;
    Operand val = Unary.getVal(s);
    if (val instanceof RegisterOperand) {
      RegisterOperand rval = (RegisterOperand) val;
      i1 = val;
      i2 = new RegisterOperand(regpool.getSecondReg(rval.getRegister()), TypeReference.Int);
    } else {
      LongConstantOperand rhs = (LongConstantOperand) val;
      i1 = IC(rhs.upper32());
      i2 = IC(rhs.lower32());
    }
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, sl1, i1)));
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, sl2, i2)));
    EMIT(MIR_Move.mutate(s, IA32_FMOV, Unary.getResult(s), sl));
  }

  /**
   * Returns the appropriate move operator based on the type of operand.
   */
  protected final Operator SSE2_MOVE(Operand o) {
    return o.isFloat() ? IA32_MOVSS : IA32_MOVSD;
  }

  /**
   * Returns the size based on the type of operand.
   */
  protected final byte SSE2_SIZE(Operand o) {
    return o.isFloat() ? DW : QW;
  }

  /**
   * Performs a long -> double/float conversion using x87 and marshalls back to XMMs.
   */
  protected final void SSE2_X87_FROMLONG(Instruction s) {
    Operand result = Unary.getResult(s);
    STORE_LONG_FOR_CONV(Unary.getVal(s));
    // conversion space allocated, contains the long to load.
    int offset = -burs.ir.stackManager.allocateSpaceForConversion();
    StackLocationOperand sl = new StackLocationOperand(true, offset, SSE2_SIZE(result));
    RegisterOperand st0 = new RegisterOperand(getST0(), result.getType());
    EMIT(CPOS(s, MIR_Move.create(IA32_FILD, st0, sl)));
    EMIT(CPOS(s, MIR_Move.create(IA32_FSTP, sl.copy(), st0.copyD2U())));
    EMIT(CPOS(s, MIR_Move.mutate(s, SSE2_MOVE(result), result, sl.copy())));
  }

  /**
   * Performs a long -> double/float conversion using x87 and marshalls between to XMMs.
   */
  protected final void SSE2_X87_REM(Instruction s) {
    Operand result = Binary.getClearResult(s);
    RegisterOperand st0 = new RegisterOperand(getST0(), result.getType());
    int offset = -burs.ir.stackManager.allocateSpaceForConversion();
    StackLocationOperand sl = new StackLocationOperand(true, offset, SSE2_SIZE(result));
    EMIT(CPOS(s, MIR_Move.create(SSE2_MOVE(result), sl, Binary.getVal2(s))));
    EMIT(CPOS(s, MIR_Move.create(IA32_FLD, st0, sl.copy())));
    EMIT(CPOS(s, MIR_Move.create(SSE2_MOVE(result), sl.copy(), Binary.getVal1(s))));
    EMIT(CPOS(s, MIR_Move.create(IA32_FLD, st0.copy(), sl.copy())));
    // The parameters to FPREM actually get ignored (implied ST0/ST1)
    EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_FPREM, st0.copy(), st0.copy())));
    EMIT(CPOS(s, MIR_Move.create(IA32_FSTP, sl.copy(), st0.copy())));
    EMIT(CPOS(s, MIR_Nullary.create(IA32_FFREE, st0.copy())));
    EMIT(MIR_Move.mutate(s, SSE2_MOVE(result), result, sl.copy()));
  }

  /**
   * Emit code to move 64 bits from SSE2 FPRs to GPRs
   */
  protected final void SSE2_FPR2GPR_64(Instruction s) {
    int offset = -burs.ir.stackManager.allocateSpaceForConversion();
    StackLocationOperand sl = new StackLocationOperand(true, offset, QW);
    StackLocationOperand sl1 = new StackLocationOperand(true, offset + 4, DW);
    StackLocationOperand sl2 = new StackLocationOperand(true, offset, DW);
    EMIT(CPOS(s, MIR_Move.create(IA32_MOVLPD, sl, Unary.getVal(s))));
    RegisterOperand i1 = Unary.getResult(s);
    RegisterOperand i2 = new RegisterOperand(regpool
        .getSecondReg(i1.getRegister()), TypeReference.Int);
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, i1, sl1)));
    EMIT(MIR_Move.mutate(s, IA32_MOV, i2, sl2));
  }

  /**
   * Emit code to move 64 bits from GPRs to SSE2 FPRs
   */
  protected final void SSE2_GPR2FPR_64(Instruction s) {
    int offset = -burs.ir.stackManager.allocateSpaceForConversion();
    StackLocationOperand sl = new StackLocationOperand(true, offset, QW);
    StackLocationOperand sl1 = new StackLocationOperand(true, offset + 4, DW);
    StackLocationOperand sl2 = new StackLocationOperand(true, offset, DW);
    Operand i1, i2;
    Operand val = Unary.getVal(s);
    if (val instanceof RegisterOperand) {
      RegisterOperand rval = (RegisterOperand) val;
      i1 = val;
      i2 = new RegisterOperand(regpool.getSecondReg(rval.getRegister()), TypeReference.Int);
    } else {
      LongConstantOperand rhs = (LongConstantOperand) val;
      i1 = IC(rhs.upper32());
      i2 = IC(rhs.lower32());
    }
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, sl1, i1)));
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, sl2, i2)));
    EMIT(MIR_Move.mutate(s, IA32_MOVLPD, Unary.getResult(s), sl));
  }

  /**
   * Emit code to move 32 bits from FPRs to GPRs
   */
  protected final void SSE2_FPR2GPR_32(Instruction s) {
    EMIT(MIR_Move.mutate(s, IA32_MOVD, Unary.getResult(s), Unary.getVal(s)));
//    int offset = -burs.ir.stackManager.allocateSpaceForConversion();
//    StackLocationOperand sl = new StackLocationOperand(true, offset, DW);
//    EMIT(CPOS(s, MIR_Move.create(IA32_MOVSS, sl, Unary.getVal(s))));
//    EMIT(MIR_Move.mutate(s, IA32_MOV, Unary.getResult(s), sl.copy()));
  }

  /**
   * Emit code to move 32 bits from GPRs to FPRs
   */
  protected final void SSE2_GPR2FPR_32(Instruction s) {
    EMIT(MIR_Move.mutate(s, IA32_MOVD, Unary.getResult(s), Unary.getVal(s)));
//    int offset = -burs.ir.stackManager.allocateSpaceForConversion();
//    StackLocationOperand sl = new StackLocationOperand(true, offset, DW);
//    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, sl, Unary.getVal(s))));
//    EMIT(MIR_Move.mutate(s, IA32_MOVSS, Unary.getResult(s), sl.copy()));
  }

  /**
   * BURS expansion of a commutative SSE2 operation.
   */
  protected void SSE2_COP(Operator operator, Instruction s, Operand result, Operand val1, Operand val2) {
    if(VM.VerifyAssertions) VM._assert(result.isRegister());
    // Swap operands to reduce chance of generating a move or to normalize
    // constants into val2
    if (val2.similar(result)) {
      Operand temp = val1;
      val1 = val2;
      val2 = temp;
    }
    // Do we need to move prior to the operator - result = val1
    if (!result.similar(val1)) {
      EMIT(CPOS(s, MIR_Move.create(SSE2_MOVE(result), result.copy(), val1)));
    }
    EMIT(MIR_BinaryAcc.mutate(s, operator, result, val2));
  }

  /**
   * BURS expansion of a non commutative SSE2 operation.
   */
  protected void SSE2_NCOP(Operator operator, Instruction s, Operand result, Operand val1, Operand val2) {
    if(VM.VerifyAssertions) VM._assert(result.isRegister());
    if (result.similar(val1)) {
      // Straight forward case where instruction is already in accumulate form
      EMIT(MIR_BinaryAcc.mutate(s, operator, result, val2));
    } else if (!result.similar(val2)) {
      // Move first operand to result and perform operator on result, if
      // possible redundant moves should be remove by register allocator
      EMIT(CPOS(s, MIR_Move.create(SSE2_MOVE(result), result.copy(), val1)));
      EMIT(MIR_BinaryAcc.mutate(s, operator, result, val2));
    } else {
      // Potential to clobber second operand during move to result. Use a
      // temporary register to perform the operation and rely on register
      // allocator to remove redundant moves
      RegisterOperand temp = regpool.makeTemp(result);
      EMIT(CPOS(s, MIR_Move.create(SSE2_MOVE(result), temp, val1)));
      EMIT(MIR_BinaryAcc.mutate(s, operator, temp.copyRO(), val2));
      EMIT(CPOS(s, MIR_Move.create(SSE2_MOVE(result), result, temp.copyRO())));
    }
  }

  /**
   * Expansion of SSE2 negation ops
   */
  protected final void SSE2_NEG(Operator xorOp, Operator subOp, Instruction s, Operand result, Operand value) {
    if(VM.VerifyAssertions) VM._assert(result.isRegister());
    if (!result.similar(value)) {
      EMIT(CPOS(s, MIR_BinaryAcc.create(xorOp, result.copy(), result.copy())));
      EMIT(MIR_BinaryAcc.mutate(s, subOp, result, value));
    } else {
      RegisterOperand temp = regpool.makeTemp(value.getType());
      EMIT(CPOS(s, MIR_BinaryAcc.create(xorOp, temp.copyRO(), temp)));
      EMIT(MIR_BinaryAcc.mutate(s, subOp, temp.copyRO(), value));
      EMIT(CPOS(s, MIR_Move.create(SSE2_MOVE(result), result, temp.copyRO())));
    }
  }

  /**
   * Expansion of SSE2 conversions double <-> float
   */
  protected final void SSE2_CONV(Operator op, Instruction s, Operand result, Operand value) {
    if(VM.VerifyAssertions) VM._assert(result.isRegister());
    EMIT(MIR_Unary.mutate(s, op, result, value));
  }

  /**
   * Expansion of SSE2 comparison operations
   */
  protected final void SSE2_IFCMP(Operator op, Instruction s, Operand val1, Operand val2) {
    EMIT(CPOS(s, MIR_Compare.create(op, val1, val2)));
    EMIT(s); // ComplexLIR2MIRExpansion will handle rest of the work.
  }

  /**
   * Expansion of SSE2 floating point constant loads
   */
  protected final void SSE2_FPCONSTANT(Instruction s) {
    RegisterOperand res = Binary.getResult(s);
    Operand val = Binary.getVal2(s); // float or double value
    if (val.isFloatConstant() && Float.floatToRawIntBits(val.asFloatConstant().value) == 0) {
      EMIT(MIR_BinaryAcc.mutate(s, IA32_XORPS, res, res.copyRO()));
    } else if (val.isDoubleConstant() && Double.doubleToRawLongBits(val.asDoubleConstant().value) == 0L) {
      EMIT(MIR_BinaryAcc.mutate(s, IA32_XORPD, res, res.copyRO()));
    }else {
      EMIT(MIR_Move.mutate(s, SSE2_MOVE(res), res, MO_MC(s)));
    }
  }

  /**
   * Expansion of INT_DIV and INT_REM
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param val1 the first operand
   * @param val2 the second operand
   * @param isDiv true for div, false for rem
   */
  protected final void INT_DIVIDES(Instruction s, RegisterOperand result, Operand val1, Operand val2,
                                   boolean isDiv) {
    if (val1.isIntConstant()) {
      int value = val1.asIntConstant().value;
      if (value < 0) {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(getEDX(), TypeReference.Int), IC(-1))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(getEAX(), TypeReference.Int), val1)));
      } else {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(getEDX(), TypeReference.Int), IC(0))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(getEAX(), TypeReference.Int), val1)));
      }
    } else {
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, new RegisterOperand(getEAX(), TypeReference.Int), val1)));
      EMIT(CPOS(s, MIR_ConvertDW2QW.create(IA32_CDQ,
                                   new RegisterOperand(getEDX(), TypeReference.Int),
                                   new RegisterOperand(getEAX(), TypeReference.Int))));
    }
    if (val2.isIntConstant()) {
      RegisterOperand temp = regpool.makeTempInt();
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, temp, val2)));
      val2 = temp.copyRO();
    }
    EMIT(MIR_Divide.mutate(s,
                           IA32_IDIV,
                           new RegisterOperand(getEDX(), TypeReference.Int),
                           new RegisterOperand(getEAX(), TypeReference.Int),
                           val2,
                           GuardedBinary.getGuard(s)));
    if (isDiv) {
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, result.copyD2D(), new RegisterOperand(getEAX(), TypeReference.Int))));
    } else {
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, result.copyD2D(), new RegisterOperand(getEDX(), TypeReference.Int))));
    }
  }

  /**
   * Expansion of LONG_ADD
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param value1 the first operand
   * @param value2 the second operand
   */
  protected final void LONG_ADD(Instruction s, RegisterOperand result,
      Operand value1, Operand value2) {
    // The value of value1 should be identical to result, to avoid moves, and a
    // register in the case of addition with a constant
    if ((value2.similar(result)) || value1.isLongConstant()) {
      Operand temp = value1;
      value1 = value2;
      value2 = temp;
    }
    Register lhsReg = result.getRegister();
    Register lowlhsReg = regpool.getSecondReg(lhsReg);
    if (value1.isRegister() && value2.isRegister()) {
      Register rhsReg1 = ((RegisterOperand) value1).getRegister();
      Register lowrhsReg1 = regpool.getSecondReg(rhsReg1);
      Register rhsReg2 = ((RegisterOperand) value2).getRegister();
      Register lowrhsReg2 = regpool.getSecondReg(rhsReg2);
      // Do we need to move prior to the add - result = value1
      if (!value1.similar(result)) {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            new RegisterOperand(rhsReg1, TypeReference.Int))));
      }
      // Perform add - result += value2
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
          new RegisterOperand(lowlhsReg, TypeReference.Int),
          new RegisterOperand(lowrhsReg2, TypeReference.Int))));
      EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_ADC,
          new RegisterOperand(lhsReg, TypeReference.Int),
          new RegisterOperand(rhsReg2, TypeReference.Int))));
    } else if (value1.isRegister()){
      Register rhsReg1 = ((RegisterOperand) value1).getRegister();
      Register lowrhsReg1 = regpool.getSecondReg(rhsReg1);
      LongConstantOperand rhs2 = (LongConstantOperand) value2;
      int low = rhs2.lower32();
      int high = rhs2.upper32();
      // Do we need to move prior to the add - result = value1
      if (!value1.similar(result)) {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            new RegisterOperand(rhsReg1, TypeReference.Int))));
      }
      // Perform add - result += value2
      if (low == 0) {
        EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_ADD,
            new RegisterOperand(lhsReg, TypeReference.Int),
            IC(high))));
      } else {
        EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(low))));
        EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_ADC,
            new RegisterOperand(lhsReg, TypeReference.Int),
            IC(high))));
      }
    } else {
      throw new OptimizingCompilerException("BURS_Helpers",
          "unexpected parameters: " + result + "=" + value1 + "+" + value2);
    }
  }

  /**
   * Expansion of LONG_SUB
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param val1 the first operand
   * @param val2 the second operand
   */
  protected final void LONG_SUB(Instruction s, Operand result, Operand val1, Operand val2) {

    if (result.similar(val1)) {
      // Straight forward case where instruction is already in accumulate form
      if (result.isRegister()) {
        Register lhsReg = result.asRegister().getRegister();
        Register lowlhsReg = regpool.getSecondReg(lhsReg);
        if (val2.isRegister()) {
          Register rhsReg2 = val2.asRegister().getRegister();
          Register lowrhsReg2 = regpool.getSecondReg(rhsReg2);
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg2, TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_SBB,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg2, TypeReference.Int))));
        } else if (val2.isLongConstant()) {
          LongConstantOperand rhs2 = val2.asLongConstant();
          int low = rhs2.lower32();
          int high = rhs2.upper32();
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              IC(low))));
          EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_SBB,
              new RegisterOperand(lhsReg, TypeReference.Int),
              IC(high))));
        } else {
          throw new OptimizingCompilerException("BURS_Helpers",
              "unexpected parameters: " + result + "=" + val1 + "-" + val2);
        }
      } else {
        throw new OptimizingCompilerException("BURS_Helpers",
            "unexpected parameters: " + result + "=" + val1 + "-" + val2);
      }
    } else if (!result.similar(val2)) {
      // Move first operand to result and perform operator on result, if
      // possible redundant moves should be remove by register allocator
      if (result.isRegister()) {
        Register lhsReg = result.asRegister().getRegister();
        Register lowlhsReg = regpool.getSecondReg(lhsReg);
        // Move val1 into result
        if (val1.isRegister()) {
          Register rhsReg1 = val1.asRegister().getRegister();
          Register lowrhsReg1 = regpool.getSecondReg(rhsReg1);
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
        } else if (val1.isLongConstant()) {
          LongConstantOperand rhs1 = val1.asLongConstant();
          int low = rhs1.lower32();
          int high = rhs1.upper32();
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              IC(low))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              IC(high))));
        } else {
          throw new OptimizingCompilerException("BURS_Helpers",
              "unexpected parameters: " + result + "=" + val1 + "-" + val2);
        }
        // Perform subtract
        if (val2.isRegister()) {
          Register rhsReg2 = val2.asRegister().getRegister();
          Register lowrhsReg2 = regpool.getSecondReg(rhsReg2);
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg2, TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_SBB,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg2, TypeReference.Int))));
        } else if (val2.isLongConstant()) {
          LongConstantOperand rhs2 = val2.asLongConstant();
          int low = rhs2.lower32();
          int high = rhs2.upper32();
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              IC(low))));
          EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_SBB,
              new RegisterOperand(lhsReg, TypeReference.Int),
              IC(high))));
        } else {
          throw new OptimizingCompilerException("BURS_Helpers",
              "unexpected parameters: " + result + "=" + val1 + "-" + val2);
        }
      } else {
        throw new OptimizingCompilerException("BURS_Helpers",
            "unexpected parameters: " + result + "=" + val1 + "-" + val2);
      }
    } else {
      // Potential to clobber second operand during move to result. Use a
      // temporary register to perform the operation and rely on register
      // allocator to remove redundant moves
      RegisterOperand temp1 = regpool.makeTempInt();
      RegisterOperand temp2 = regpool.makeTempInt();
      // Move val1 into temp
      if (val1.isRegister()) {
        Register rhsReg1 = val1.asRegister().getRegister();
        Register lowrhsReg1 = regpool.getSecondReg(rhsReg1);
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            temp1,
            new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            temp2,
            new RegisterOperand(rhsReg1, TypeReference.Int))));
      } else if (val1.isLongConstant()) {
        LongConstantOperand rhs1 = val1.asLongConstant();
        int low = rhs1.lower32();
        int high = rhs1.upper32();
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            temp1,
            IC(low))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            temp2,
            IC(high))));
      } else {
        throw new OptimizingCompilerException("BURS_Helpers",
            "unexpected parameters: " + result + "=" + val1 + "-" + val2);
      }
      // Perform subtract
      if (val2.isRegister()) {
        Register rhsReg2 = val2.asRegister().getRegister();
        Register lowrhsReg2 = regpool.getSecondReg(rhsReg2);
        EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB,
            temp1.copyRO(),
            new RegisterOperand(lowrhsReg2, TypeReference.Int))));
        EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_SBB,
            temp2.copyRO(),
            new RegisterOperand(rhsReg2, TypeReference.Int))));
      } else if (val2.isLongConstant()) {
        LongConstantOperand rhs2 = val2.asLongConstant();
        int low = rhs2.lower32();
        int high = rhs2.upper32();
        EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB,
            temp1.copyRO(),
            IC(low))));
        EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_SBB,
            temp2.copyRO(),
            IC(high))));
      } else {
        throw new OptimizingCompilerException("BURS_Helpers",
            "unexpected parameters: " + result + "=" + val1 + "-" + val2);
      }
      // Move result back
      if (result.isRegister()) {
        Register lhsReg = result.asRegister().getRegister();
        Register lowlhsReg = regpool.getSecondReg(lhsReg);
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            temp1.copyRO())));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            temp2.copyRO())));
      } else {
        throw new OptimizingCompilerException("BURS_Helpers",
            "unexpected parameters: " + result + "=" + val1 + "-" + val2);
      }
    }
  }

  /**
   * Expansion of LONG_MUL
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param value1 the first operand
   * @param value2 the second operand
   */
  protected final void LONG_MUL(Instruction s, RegisterOperand result,
      Operand value1, Operand value2) {
    if (value2.isRegister()) {
      // Leave for complex LIR2MIR expansion as the most efficient form requires
      // a branch
      if (VM.VerifyAssertions) VM._assert(Binary.getResult(s).similar(result) &&
          Binary.getVal1(s).similar(value1) && Binary.getVal2(s).similar(value2));
      EMIT(s);
    } else {
      // The value of value1 should be identical to result, to avoid moves, and a
      // register in the case of multiplication with a constant
      if ((value2.similar(result)) || value1.isLongConstant()) {
        Operand temp = value1;
        value1 = value2;
        value2 = temp;
      }
      if (VM.VerifyAssertions) VM._assert(value1.isRegister() && value2.isLongConstant());

      // In general, (a,b) * (c,d) = (l(a imul d)+l(b imul c)+u(b mul d), l(b mul d))

      Register lhsReg = result.getRegister();
      Register lowlhsReg = regpool.getSecondReg(lhsReg);

      LongConstantOperand rhs2 = (LongConstantOperand) value2;
      Register rhsReg1 = value1.asRegister().getRegister(); // a
      Register lowrhsReg1 = regpool.getSecondReg(rhsReg1); // b
      int high2 = rhs2.upper32(); // c
      int low2 = rhs2.lower32(); // d

      // We only have to handle those cases that Simplifier wouldn't get.
      // Simplifier catches
      // high low
      // 0 0 (0L)
      // 0 1 (1L)
      // -1 -1 (-1L)
      // So, the possible cases we need to handle here:
      // -1 0
      // -1 1
      // -1 *
      // 0 -1
      // 0 *
      // 1 -1
      // 1 0
      // 1 1
      // 1 *
      // * -1
      // * 0
      // * 1
      // * *
      // (where * is something other than -1,0,1)
      if (high2 == -1) {
        if (low2 == 0) {
          // -1, 0
          // CLAIM: (a,b) * (-1,0) = (-b,0)
          if (VM.VerifyAssertions) VM._assert(lhsReg != lowrhsReg1);
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NEG,
              new RegisterOperand(lhsReg, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              IC(0))));
        } else if (low2 == 1) {
          // -1, 1
          // CLAIM: (a,b) * (-1,1) = (a-b,b)
          if (lowlhsReg != lowrhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          }
          if (lhsReg != rhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new RegisterOperand(rhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(lowlhsReg, TypeReference.Int))));
        } else {
          // -1, *
          // CLAIM: (a,b) * (-1, d) = (l(a imul d)-b+u(b mul d), l(b mul d))
          if (lhsReg != rhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new RegisterOperand(rhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_IMUL2,
              new RegisterOperand(lhsReg, TypeReference.Int),
              IC(low2))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(getEAX(), TypeReference.Int),
              IC(low2))));
          EMIT(CPOS(s, MIR_Multiply.create(IA32_MUL,
              new RegisterOperand(getEDX(), TypeReference.Int),
              new RegisterOperand(getEAX(), TypeReference.Int),
              new RegisterOperand(lowlhsReg, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(getEAX(), TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(getEDX(), TypeReference.Int))));
        }
      } else if (high2 == 0) {
        if (low2 == -1) {
          // 0, -1
          // CLAIM: (a,b) * (0,-1) = (b-(a+(b!=0?1:0)),-b)
          // avoid clobbering a and b by using tmp
          Register tmp = regpool.getInteger();
          if (lowlhsReg != lowrhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(tmp, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NEG,
              new RegisterOperand(lowlhsReg, TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB,
              new RegisterOperand(tmp, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(tmp, TypeReference.Int))));
        } else {
          // 0, *
          // CLAIM: (a,b) * (0,d) = (l(a imul d)+u(b mul d), l(b mul d))
          if (lhsReg != rhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new RegisterOperand(rhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_IMUL2,
              new RegisterOperand(lhsReg, TypeReference.Int),
              IC(low2))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(getEAX(), TypeReference.Int),
              IC(low2))));
          EMIT(CPOS(s, MIR_Multiply.create(IA32_MUL,
              new RegisterOperand(getEDX(), TypeReference.Int),
              new RegisterOperand(getEAX(), TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(getEAX(), TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(getEDX(), TypeReference.Int))));
        }
      } else if (high2 == 1) {
        if (low2 == -1) {
          // 1, -1
          // CLAIM: (a,b) * (1,-1) = (2b-(a+(b!=0?1:0)),-b)
          // avoid clobbering a and b by using tmp
          Register tmp = regpool.getInteger();
          if (lowlhsReg != lowrhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(tmp, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
              new RegisterOperand(tmp, TypeReference.Int),
              new RegisterOperand(tmp, TypeReference.Int))));
          EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NEG,
              new RegisterOperand(lowlhsReg, TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB,
              new RegisterOperand(tmp, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(tmp, TypeReference.Int))));
        } else if (low2 == 0) {
          // 1, 0
          // CLAIM: (x,y) * (1,0) = (y,0)
          // NB we should have simplified this LONG_MUL to a LONG_SHIFT
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              IC(0))));
        } else if (low2 == 1) {
          // 1, 1
          // CLAIM: (x,y) * (1,1) = (x+y,y)
          // NB we should have simplified this LONG_MUL to a LONG_SHIFT and LONG_ADDs
          if (lowlhsReg != lowrhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          }
          if (lhsReg != rhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new RegisterOperand(rhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(lowlhsReg, TypeReference.Int))));
        } else {
          // 1, *
          // CLAIM: (a,b) * (1,d) = (l(a imul d)+b+u(b mul d), l(b mul d))
          if (lhsReg != rhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new RegisterOperand(rhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_IMUL2,
              new RegisterOperand(lhsReg, TypeReference.Int),
              IC(low2))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(getEAX(), TypeReference.Int),
              IC(low2))));
          EMIT(CPOS(s, MIR_Multiply.create(IA32_MUL,
              new RegisterOperand(getEDX(), TypeReference.Int),
              new RegisterOperand(getEAX(), TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(getEAX(), TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(getEDX(), TypeReference.Int))));
        }
      } else {
        if (low2 == -1) {
          // *, -1
          // CLAIM: (a,b) * (c, -1) = ((b+1)*c - (a + b==0?1:0), -b)
          // avoid clobbering a and b by using tmp
          Register tmp = regpool.getInteger();
          if (lowlhsReg != lowrhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(tmp, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
              new RegisterOperand(tmp, TypeReference.Int),
              IC(1))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_IMUL2,
              new RegisterOperand(tmp, TypeReference.Int),
              IC(high2))));
          EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NEG,
              new RegisterOperand(lowlhsReg, TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB,
              new RegisterOperand(tmp, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(tmp, TypeReference.Int))));
        } else if (low2 == 0) {
          // *, 0
          // CLAIM: (a,b) * (c,0) = (l(b imul c),0)
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_IMUL2,
              new RegisterOperand(lhsReg, TypeReference.Int),
              IC(high2))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              IC(0))));
        } else if (low2 == 1) {
          // *, 1
          // CLAIM: (x,y) * (z,1) = (l(y imul z)+x,y)
          if (lowlhsReg != lowrhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          }
          if (lhsReg != rhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new RegisterOperand(rhsReg1, TypeReference.Int))));
          }
          Register tmp = regpool.getInteger();
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(tmp, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_IMUL2,
              new RegisterOperand(tmp, TypeReference.Int),
              IC(high2))));
          EMIT(CPOS(s, MIR_Move.create(IA32_ADD,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(tmp, TypeReference.Int))));
        } else {
          // *, * can't do anything interesting and both operands have non-zero words
          // (a,b) * (c,d) = (l(a imul d)+l(b imul c)+u(b mul d), l(b mul d))
          if (lhsReg != rhsReg1) {
            EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new RegisterOperand(rhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_IMUL2,
              new RegisterOperand(lhsReg, TypeReference.Int),
              IC(low2))));
          Register tmp = regpool.getInteger();
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(tmp, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_IMUL2,
              new RegisterOperand(tmp, TypeReference.Int),
              IC(high2))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(tmp, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(getEAX(), TypeReference.Int),
              IC(low2))));
          EMIT(CPOS(s, MIR_Multiply.create(IA32_MUL,
              new RegisterOperand(getEDX(), TypeReference.Int),
              new RegisterOperand(getEAX(), TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(getEAX(), TypeReference.Int))));
          EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_ADD,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(getEDX(), TypeReference.Int))));
        }
      }
    }
  }

  /**
   * Expansion of LONG_NEG
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param value the first operand
   */
  protected final void LONG_NEG(Instruction s, RegisterOperand result, Operand value) {
    Register lhsReg = result.getRegister();
    Register lowlhsReg = regpool.getSecondReg(lhsReg);
    // Move value into result if its not already
    if (!result.similar(value)){
      if (value.isRegister()) {
        Register rhsReg = value.asRegister().getRegister();
        Register lowrhsReg = regpool.getSecondReg(rhsReg);
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            new RegisterOperand(lowrhsReg, TypeReference.Int))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            new RegisterOperand(rhsReg, TypeReference.Int))));
      } else {
        throw new OptimizingCompilerException("BURS_Helpers",
            "unexpected parameters: " + result + "= -" + value);
      }
    }
    // Perform negation
    EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NOT,
        new RegisterOperand(lhsReg, TypeReference.Int))));
    EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NEG,
        new RegisterOperand(lowlhsReg, TypeReference.Int))));
    EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB,
        new RegisterOperand(lhsReg, TypeReference.Int),
        IC(-1))));
  }

  /**
   * Expansion of LONG_NOT
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param value the first operand
   */
  protected final void LONG_NOT(Instruction s, RegisterOperand result, Operand value) {
    Register lhsReg = result.getRegister();
    Register lowlhsReg = regpool.getSecondReg(lhsReg);
    // Move value into result if its not already
    if (!result.similar(value)){
      if (value.isRegister()) {
        Register rhsReg = value.asRegister().getRegister();
        Register lowrhsReg = regpool.getSecondReg(rhsReg);
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            new RegisterOperand(lowrhsReg, TypeReference.Int))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            new RegisterOperand(rhsReg, TypeReference.Int))));
      } else {
        throw new OptimizingCompilerException("BURS_Helpers",
            "unexpected parameters: " + result + "= ~" + value);
      }
    }
    // Perform not
    EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NOT,
        new RegisterOperand(lhsReg, TypeReference.Int))));
    EMIT(CPOS(s, MIR_UnaryAcc.mutate(s, IA32_NOT,
        new RegisterOperand(lowlhsReg, TypeReference.Int))));
  }

  /**
   * Expansion of LONG_AND
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param value1 the first operand
   * @param value2 the second operand
   */
  protected final void LONG_AND(Instruction s, RegisterOperand result,
      Operand value1, Operand value2) {
    // The value of value1 should be identical to result, to avoid moves, and a
    // register in the case of addition with a constant
    if ((value2.similar(result)) || value1.isLongConstant()) {
      Operand temp = value1;
      value1 = value2;
      value2 = temp;
    }
    Register lhsReg = result.getRegister();
    Register lowlhsReg = regpool.getSecondReg(lhsReg);
    if (value1.isRegister() && value2.isRegister()) {
      Register rhsReg1 = ((RegisterOperand) value1).getRegister();
      Register lowrhsReg1 = regpool.getSecondReg(rhsReg1);
      Register rhsReg2 = ((RegisterOperand) value2).getRegister();
      Register lowrhsReg2 = regpool.getSecondReg(rhsReg2);
      // Do we need to move prior to the and - result = value1
      if (!value1.similar(result)) {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            new RegisterOperand(rhsReg1, TypeReference.Int))));
      }
      // Perform and - result &= value2
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_AND,
          new RegisterOperand(lowlhsReg, TypeReference.Int),
          new RegisterOperand(lowrhsReg2, TypeReference.Int))));
      EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_AND,
          new RegisterOperand(lhsReg, TypeReference.Int),
          new RegisterOperand(rhsReg2, TypeReference.Int))));
    } else if (value1.isRegister()){
      Register rhsReg1 = ((RegisterOperand) value1).getRegister();
      Register lowrhsReg1 = regpool.getSecondReg(rhsReg1);
      LongConstantOperand rhs2 = (LongConstantOperand) value2;
      int low = rhs2.lower32();
      int high = rhs2.upper32();
      // Do we need to move prior to the and - result = value1
      if (!value1.similar(result)) {
        if (low != 0) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        }
        if (high != 0) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
      }
      // Perform and - result &= value2
      if (low == 0) {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(0))));
      } else if (low == -1) {
        // nop
      } else {
        EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_AND,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(low))));
      }
      if (high == 0) {
        EMIT(CPOS(s, MIR_Move.mutate(s, IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            IC(0))));
      } else if (high == -1) {
        // nop
      } else {
        EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_AND,
            new RegisterOperand(lhsReg, TypeReference.Int),
            IC(high))));
      }
    } else {
      throw new OptimizingCompilerException("BURS_Helpers",
          "unexpected parameters: " + result + "=" + value1 + "+" + value2);
    }
  }
  /**
   * Expansion of LONG_OR
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param value1 the first operand
   * @param value2 the second operand
   */
  protected final void LONG_OR(Instruction s, RegisterOperand result,
      Operand value1, Operand value2) {
    // The value of value1 should be identical to result, to avoid moves, and a
    // register in the case of addition with a constant
    if ((value2.similar(result)) || value1.isLongConstant()) {
      Operand temp = value1;
      value1 = value2;
      value2 = temp;
    }
    Register lhsReg = result.getRegister();
    Register lowlhsReg = regpool.getSecondReg(lhsReg);
    if (value1.isRegister() && value2.isRegister()) {
      Register rhsReg1 = ((RegisterOperand) value1).getRegister();
      Register lowrhsReg1 = regpool.getSecondReg(rhsReg1);
      Register rhsReg2 = ((RegisterOperand) value2).getRegister();
      Register lowrhsReg2 = regpool.getSecondReg(rhsReg2);
      // Do we need to move prior to the and - result = value1
      if (!value1.similar(result)) {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            new RegisterOperand(rhsReg1, TypeReference.Int))));
      }
      // Perform or - result |= value2
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_OR,
          new RegisterOperand(lowlhsReg, TypeReference.Int),
          new RegisterOperand(lowrhsReg2, TypeReference.Int))));
      EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_OR,
          new RegisterOperand(lhsReg, TypeReference.Int),
          new RegisterOperand(rhsReg2, TypeReference.Int))));
    } else if (value1.isRegister()){
      Register rhsReg1 = ((RegisterOperand) value1).getRegister();
      Register lowrhsReg1 = regpool.getSecondReg(rhsReg1);
      LongConstantOperand rhs2 = (LongConstantOperand) value2;
      int low = rhs2.lower32();
      int high = rhs2.upper32();
      // Do we need to move prior to the and - result = value1
      if (!value1.similar(result)) {
        if (low != -1) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        }
        if (high != -1) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
      }
      // Perform or - result |= value2
      if (low == 0) {
        // nop
      } else if (low == -1) {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(-1))));
      } else {
        EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_OR,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(low))));
      }
      if (high == 0) {
        // nop
      } else if (high == -1) {
        EMIT(CPOS(s, MIR_Move.mutate(s, IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            IC(-1))));
      } else {
        EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_OR,
            new RegisterOperand(lhsReg, TypeReference.Int),
            IC(high))));
      }
    } else {
      throw new OptimizingCompilerException("BURS_Helpers",
          "unexpected parameters: " + result + "=" + value1 + "+" + value2);
    }
  }
  /**
   * Expansion of LONG_XOR
   *
   * @param s the instruction to expand
   * @param result the result operand
   * @param value1 the first operand
   * @param value2 the second operand
   */
  protected final void LONG_XOR(Instruction s, RegisterOperand result,
      Operand value1, Operand value2) {
    // The value of value1 should be identical to result, to avoid moves, and a
    // register in the case of addition with a constant
    if ((value2.similar(result)) || value1.isLongConstant()) {
      Operand temp = value1;
      value1 = value2;
      value2 = temp;
    }
    Register lhsReg = result.getRegister();
    Register lowlhsReg = regpool.getSecondReg(lhsReg);
    if (value1.isRegister() && value2.isRegister()) {
      Register rhsReg1 = ((RegisterOperand) value1).getRegister();
      Register lowrhsReg1 = regpool.getSecondReg(rhsReg1);
      Register rhsReg2 = ((RegisterOperand) value2).getRegister();
      Register lowrhsReg2 = regpool.getSecondReg(rhsReg2);
      // Do we need to move prior to the and - result = value1
      if (!value1.similar(result)) {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            new RegisterOperand(rhsReg1, TypeReference.Int))));
      }
      // Perform or - result |= value2
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_XOR,
          new RegisterOperand(lowlhsReg, TypeReference.Int),
          new RegisterOperand(lowrhsReg2, TypeReference.Int))));
      EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_XOR,
          new RegisterOperand(lhsReg, TypeReference.Int),
          new RegisterOperand(rhsReg2, TypeReference.Int))));
    } else if (value1.isRegister()){
      Register rhsReg1 = ((RegisterOperand) value1).getRegister();
      Register lowrhsReg1 = regpool.getSecondReg(rhsReg1);
      LongConstantOperand rhs2 = (LongConstantOperand) value2;
      int low = rhs2.lower32();
      int high = rhs2.upper32();
      // Do we need to move prior to the and - result = value1
      if (!value1.similar(result)) {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(lhsReg, TypeReference.Int),
            new RegisterOperand(rhsReg1, TypeReference.Int))));
      }
      // Perform xor - result ^= value2
      if (low == 0) {
        // nop
      } else if (low == -1) {
        EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NOT,
            new RegisterOperand(lowlhsReg, TypeReference.Int))));
      } else {
        EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_XOR,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(low))));
      }
      if (high == 0) {
        // nop
      } else if (high == -1) {
        EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NOT,
            new RegisterOperand(lhsReg, TypeReference.Int))));
      } else {
        EMIT(CPOS(s, MIR_BinaryAcc.mutate(s, IA32_XOR,
            new RegisterOperand(lhsReg, TypeReference.Int),
            IC(high))));
      }
    } else {
      throw new OptimizingCompilerException("BURS_Helpers",
          "unexpected parameters: " + result + "=" + value1 + "+" + value2);
    }
  }

  /**
   * Expansion of LONG_SHL
   * @param s the instruction to expand
   * @param result the result operand
   * @param val1 the shifted operand
   * @param val2 the shift amount operand
   * @param maskWith3f should the shift operand by masked with 0x3f? This is
   *          default behaviour on Intel but it differs from how we combine
   *          shift operands in HIR
   */
  protected final void LONG_SHL(Instruction s, Operand result,
      Operand val1, Operand val2, boolean maskWith3f) {
    if (!val2.isIntConstant()) {
      // the most efficient form of expanding a shift by a variable amount
      // requires a branch so leave for complex operators
      // NB if !maskWith3f - we assume that a mask with 0x3F was required as
      // no optimizations currently exploits shift by registers of > 63
      // returning 0
      Binary.mutate(s, LONG_SHL, result.asRegister(), val1, val2);
      EMIT(s);
    } else if (result.isRegister()) {
      int shift = val2.asIntConstant().value;
      Register lhsReg = result.asRegister().getRegister();
      Register lowlhsReg = burs.ir.regpool.getSecondReg(lhsReg);
      Register rhsReg1 = val1.asRegister().getRegister();
      Register lowrhsReg1 = burs.ir.regpool.getSecondReg(rhsReg1);

      if (shift == 0) {
        // operation is a nop.
        if (!result.similar(val1)) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
      } else if (shift == 1) {
        if (!result.similar(val1)) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_ADD,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new RegisterOperand(lowlhsReg, TypeReference.Int))));
        EMIT(MIR_BinaryAcc.mutate(s,
            IA32_ADC,
            new RegisterOperand(lhsReg, TypeReference.Int),
            new RegisterOperand(lhsReg, TypeReference.Int)));
      } else if (shift == 2) {
        // bits to shift in: tmp = lowrhsReg >> 30
        Register tmp = regpool.getInteger();
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(tmp, TypeReference.Int),
            new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SHR,
                new RegisterOperand(tmp, TypeReference.Int),
                IC(30))));
        // compute top half: lhsReg = (rhsReg1 << 2) + tmp
        EMIT(CPOS(s,
            MIR_Lea.create(IA32_LEA,
                new RegisterOperand(lhsReg, TypeReference.Int),
                MemoryOperand.BIS(new RegisterOperand(tmp, TypeReference.Int),
                    new RegisterOperand(rhsReg1, TypeReference.Int),
                    (byte)2, (byte)4, null, null))));
        // compute bottom half: lowlhsReg = lowlhsReg << 2
        EMIT(CPOS(s,
            MIR_Lea.create(IA32_LEA,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new MemoryOperand(null, // base
                    new RegisterOperand(lowrhsReg1, TypeReference.Int), //index
                    (byte)2, // scale
                    Offset.zero(), // displacement
                    (byte)4, // size
                    null, // location
                    null // guard
                    ))));
      } else if (shift == 3) {
        // bits to shift in: tmp = lowrhsReg >>> 29
        Register tmp = regpool.getInteger();
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
            new RegisterOperand(tmp, TypeReference.Int),
            new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SHR,
                new RegisterOperand(tmp, TypeReference.Int),
                IC(29))));
        // compute top half: lhsReg = (rhsReg1 << 3) + tmp
        EMIT(CPOS(s,
            MIR_Lea.create(IA32_LEA,
                new RegisterOperand(lhsReg, TypeReference.Int),
                MemoryOperand.BIS(new RegisterOperand(tmp, TypeReference.Int),
                    new RegisterOperand(rhsReg1, TypeReference.Int),
                    (byte)3, (byte)4, null, null))));
        // compute bottom half: lowlhsReg = lowlhsReg << 3
        EMIT(CPOS(s,
            MIR_Lea.create(IA32_LEA,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new MemoryOperand(null, // base
                    new RegisterOperand(lowrhsReg1, TypeReference.Int), //index
                    (byte)3, // scale
                    Offset.zero(), // displacement
                    (byte)4, // size
                    null, // location
                    null // guard
                    ))));
      } else if (shift < 32) {
        if (!result.similar(val1)) {
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
        // bits to shift in: tmp = lowrhsReg >>> (32 - shift)
        Register tmp = regpool.getInteger();
        EMIT(CPOS(s,
            MIR_Move.create(IA32_MOV,
                new RegisterOperand(tmp, TypeReference.Int),
                new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SHR,
                new RegisterOperand(tmp, TypeReference.Int),
                IC(32 - shift))));
        // compute top half: lhsReg = (lhsReg1 << shift) | tmp
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SHL,
                new RegisterOperand(lhsReg, TypeReference.Int),
                IC(shift))));
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_OR,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new RegisterOperand(tmp, TypeReference.Int))));
        // compute bottom half: lowlhsReg = lowlhsReg << shift
        if (!result.similar(val1)) {
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        }
        EMIT(MIR_BinaryAcc.mutate(s, IA32_SHL,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                IC(shift)));
      } else if (shift == 32) {
        // lhsReg = lowrhsReg1
        EMIT(CPOS(s,
            MIR_Move.create(IA32_MOV,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        // lowlhsReg = 0
        EMIT(MIR_Move.mutate(s, IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(0)));
      } else if (shift == 33) {
        // lhsReg = lowrhsReg1 << 1
        EMIT(CPOS(s,
            MIR_Lea.create(IA32_LEA,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new MemoryOperand(null, // base
                    new RegisterOperand(lowrhsReg1, TypeReference.Int), //index
                    (byte)1, // scale
                    Offset.zero(), // displacement
                    (byte)4, // size
                    null, // location
                    null // guard
                    ))));
        // lowlhsReg = 0
        EMIT(MIR_Move.mutate(s, IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(0)));
      } else if (shift == 34) {
        // lhsReg = lowrhsReg1 << 2
        EMIT(CPOS(s,
            MIR_Lea.create(IA32_LEA,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new MemoryOperand(null, // base
                    new RegisterOperand(lowrhsReg1, TypeReference.Int), //index
                    (byte)2, // scale
                    Offset.zero(), // displacement
                    (byte)4, // size
                    null, // location
                    null // guard
                    ))));
        // lowlhsReg = 0
        EMIT(MIR_Move.mutate(s, IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(0)));
      } else if (shift == 35) {
        // lhsReg = lowrhsReg1 << 3
        EMIT(CPOS(s,
            MIR_Lea.create(IA32_LEA,
                new RegisterOperand(lhsReg, TypeReference.Int),
                new MemoryOperand(null, // base
                    new RegisterOperand(lowrhsReg1, TypeReference.Int), //index
                    (byte)3, // scale
                    Offset.zero(), // displacement
                    (byte)4, // size
                    null, // location
                    null // guard
                    ))));
        // lowlhsReg = 0
        EMIT(MIR_Move.mutate(s, IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(0)));
      } else {
        if ((maskWith3f) || (shift < 64)){
          // lhsReg = lowrhsReg1 << ((shift - 32) & 0x1f)
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
                  new RegisterOperand(lhsReg, TypeReference.Int),
                  new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s,
              MIR_BinaryAcc.create(IA32_SHL,
                  new RegisterOperand(lhsReg, TypeReference.Int),
                  IC((shift-32) & 0x1F))));
          // lowlhsReg = 0
          EMIT(MIR_Move.mutate(s, IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              IC(0)));
        } else {
          // lhsReg = 0
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              IC(0))));
          // lowlhsReg = 0
          EMIT(MIR_Move.mutate(s, IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              IC(0)));
        }
      }
    } else {
      throw new OptimizingCompilerException("BURS_Helpers",
          "unexpected parameters: " + result + "=" + val1 + "<<" + val2);
    }
  }

  /**
   * Expansion of LONG_SHR
   * @param s the instruction to expand
   * @param result the result operand
   * @param val1 the shifted operand
   * @param val2 the shift amount operand
   * @param maskWith3f should the shift operand by masked with 0x3f? This is
   *          default behaviour on Intel but it differs from how we combine
   *          shift operands in HIR
   */
  protected final void LONG_SHR(Instruction s, Operand result,
      Operand val1, Operand val2, boolean maskWith3f) {
    if (!val2.isIntConstant()) {
      // the most efficient form of expanding a shift by a variable amount
      // requires a branch so leave for complex operators
      // NB if !maskWith3f - we assume that a mask with 0x3F was required as
      // no optimizations currently exploits shift by registers of > 63
      // returning 0
      Binary.mutate(s, LONG_SHR, result.asRegister(), val1, val2);
      EMIT(s);
    } else if (result.isRegister()) {
      int shift = val2.asIntConstant().value;
      if (maskWith3f) {
        shift = shift & 0x3F;
      }
      Register lhsReg = result.asRegister().getRegister();
      Register lowlhsReg = burs.ir.regpool.getSecondReg(lhsReg);
      Register rhsReg1 = val1.asRegister().getRegister();
      Register lowrhsReg1 = burs.ir.regpool.getSecondReg(rhsReg1);

      if (shift == 0) {
        // operation is a nop.
        if (!result.similar(val1)) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
      } else if (shift == 1) {
        if (!result.similar(val1)) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
        // lhsReg = lhsReg >> 1
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SAR,
                new RegisterOperand(lhsReg, TypeReference.Int),
                IC(1))));
        // lowlhsReg = (lhsReg << 31) | (lowlhsReg >>> 1)
        EMIT(MIR_BinaryAcc.mutate(s, IA32_RCR,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(1)));
      } else if (shift < 32) {
        // bits to shift in: tmp = rhsReg << (32 - shift)
        // TODO: use of LEA for SHL
        Register tmp = regpool.getInteger();
        EMIT(CPOS(s,
            MIR_Move.create(IA32_MOV,
                new RegisterOperand(tmp, TypeReference.Int),
                new RegisterOperand(rhsReg1, TypeReference.Int))));
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SHL,
                new RegisterOperand(tmp, TypeReference.Int),
                IC(32 - shift))));
        // compute bottom half: lowlhsReg = (lowlhsReg1 >>> shift) | tmp
        if (!result.similar(val1)) {
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
                  new RegisterOperand(lowlhsReg, TypeReference.Int),
                  new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        }
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SHR,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                IC(shift))));
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_OR,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new RegisterOperand(tmp, TypeReference.Int))));
        // compute top half: lhsReg = lhsReg >> shift
        if (!result.similar(val1)) {
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
                  new RegisterOperand(lhsReg, TypeReference.Int),
                  new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
        EMIT(MIR_BinaryAcc.mutate(s, IA32_SAR,
            new RegisterOperand(lhsReg, TypeReference.Int),
            IC(shift)));
      } else if (shift == 32) {
        // lowlhsReg = rhsReg1
        EMIT(MIR_Move.mutate(s, IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            new RegisterOperand(rhsReg1, TypeReference.Int)));
        // lhsReg = rhsReg1 >> 31
        if (!result.similar(val1)) {
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
                  new RegisterOperand(lhsReg, TypeReference.Int),
                  new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SAR,
                new RegisterOperand(lhsReg, TypeReference.Int),
                IC(31))));
      } else {
        if ((!maskWith3f && (shift >= 0x3F))||
            (maskWith3f && ((shift & 0x3F) == 0x3F))) {
          // lhsReg = rhsReg1 >> 31
          if (!result.similar(val1)) {
            EMIT(CPOS(s,
                MIR_Move.create(IA32_MOV,
                    new RegisterOperand(lhsReg, TypeReference.Int),
                    new RegisterOperand(rhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s,
              MIR_BinaryAcc.create(IA32_SAR,
                  new RegisterOperand(lhsReg, TypeReference.Int),
                  IC(31))));
          // lowlhsReg = lhsReg
          EMIT(MIR_Move.mutate(s, IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lhsReg, TypeReference.Int)));
        } else {
          // lhsReg = rhsReg1 >> 31
          if (!result.similar(val1)) {
            EMIT(CPOS(s,
                MIR_Move.create(IA32_MOV,
                    new RegisterOperand(lhsReg, TypeReference.Int),
                    new RegisterOperand(rhsReg1, TypeReference.Int))));
          }
          EMIT(CPOS(s,
              MIR_BinaryAcc.create(IA32_SAR,
                  new RegisterOperand(lhsReg, TypeReference.Int),
                  IC(31))));
          // lowlhsReg = rhsReg1 >> shift
          EMIT(MIR_Move.mutate(s, IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int)));
          EMIT(CPOS(s,
              MIR_BinaryAcc.create(IA32_SAR,
                  new RegisterOperand(lowlhsReg, TypeReference.Int),
                  IC((shift - 32) & 0x3F))));
        }
      }
    } else {
      throw new OptimizingCompilerException("BURS_Helpers",
          "unexpected parameters: " + result + "=" + val1 + ">>" + val2);
    }
  }

  /**
   * Expansion of LONG_USHR
   * @param s the instruction to expand
   * @param result the result operand
   * @param val1 the shifted operand
   * @param val2 the shift amount operand
   * @param maskWith3f should the shift operand by masked with 0x3f? This is
   *          default behaviour on Intel but it differs from how we combine
   *          shift operands in HIR
   */
  protected final void LONG_USHR(Instruction s, Operand result,
      Operand val1, Operand val2, boolean maskWith3f) {
    if (!val2.isIntConstant()) {
      // the most efficient form of expanding a shift by a variable amount
      // requires a branch so leave for complex operators
      // NB if !maskWith3f - we assume that a mask with 0x3F was required as
      // no optimizations currently exploits shift by registers of > 63
      // returning 0
      Binary.mutate(s, LONG_USHR, result.asRegister(), val1, val2);
      EMIT(s);
    } else if (result.isRegister()) {
      int shift = val2.asIntConstant().value;
      if (maskWith3f) {
        shift = shift & 0x3F;
      }
      Register lhsReg = result.asRegister().getRegister();
      Register lowlhsReg = burs.ir.regpool.getSecondReg(lhsReg);
      Register rhsReg1 = val1.asRegister().getRegister();
      Register lowrhsReg1 = burs.ir.regpool.getSecondReg(rhsReg1);

      if (shift == 0) {
        // operation is a nop.
        if (!result.similar(val1)) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
      } else if (shift == 1) {
        if (!result.similar(val1)) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(lowrhsReg1, TypeReference.Int))));
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
              new RegisterOperand(lhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
        // lhsReg = lhsReg >>> 1
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SHR,
                new RegisterOperand(lhsReg, TypeReference.Int),
                IC(1))));
        // lowlhsReg = (lhsReg << 31) | (lowlhsReg >>> 1)
        EMIT(MIR_BinaryAcc.mutate(s, IA32_RCR,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            IC(1)));
      } else if (shift < 32) {
        // bits to shift in: tmp = rhsReg << (32 - shift)
        // TODO: use LEA for SHL operator
        Register tmp = regpool.getInteger();
        EMIT(CPOS(s,
            MIR_Move.create(IA32_MOV,
                new RegisterOperand(tmp, TypeReference.Int),
                new RegisterOperand(rhsReg1, TypeReference.Int))));
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SHL,
                new RegisterOperand(tmp, TypeReference.Int),
                IC(32 - shift))));
        // compute bottom half: lowlhsReg = (lowlhsReg1 >>> shift) | tmp
        if (!result.similar(val1)) {
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
                  new RegisterOperand(lowlhsReg, TypeReference.Int),
                  new RegisterOperand(lowrhsReg1, TypeReference.Int))));
        }
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_SHR,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                IC(shift))));
        EMIT(CPOS(s,
            MIR_BinaryAcc.create(IA32_OR,
                new RegisterOperand(lowlhsReg, TypeReference.Int),
                new RegisterOperand(tmp, TypeReference.Int))));
        // compute top half: lhsReg = lhsReg >>> shift
        if (!result.similar(val1)) {
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
                  new RegisterOperand(lhsReg, TypeReference.Int),
                  new RegisterOperand(rhsReg1, TypeReference.Int))));
        }
        EMIT(MIR_BinaryAcc.mutate(s, IA32_SHR,
            new RegisterOperand(lhsReg, TypeReference.Int),
            IC(shift)));
      } else if (shift == 32) {
        // lowlhsReg = rhsReg1
        EMIT(MIR_Move.mutate(s, IA32_MOV,
            new RegisterOperand(lowlhsReg, TypeReference.Int),
            new RegisterOperand(rhsReg1, TypeReference.Int)));
        // lhsReg = 0
        EMIT(CPOS(s,
            MIR_Move.create(IA32_MOV,
                new RegisterOperand(lhsReg, TypeReference.Int),
                IC(0))));
      } else {
        if (maskWith3f || (shift < 64)) {
          // lowlhsReg = rhsReg1 >>> (shift & 0x1F)
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              new RegisterOperand(rhsReg1, TypeReference.Int))));
          EMIT(CPOS(s,
              MIR_BinaryAcc.create(IA32_SHR,
              new RegisterOperand(lowlhsReg, TypeReference.Int),
              IC(shift&0x1F))));
        } else {
          // lowlhsReg = 0
          EMIT(CPOS(s,
              MIR_Move.create(IA32_MOV,
                  new RegisterOperand(lowlhsReg, TypeReference.Int),
                  IC(0))));
        }
        // lhsReg = 0
        EMIT(MIR_Move.mutate(s, IA32_MOV,
                new RegisterOperand(lhsReg, TypeReference.Int),
                IC(0)));
      }
    } else {
      throw new OptimizingCompilerException("BURS_Helpers",
          "unexpected parameters: " + result + "=" + val1 + ">>" + val2);
    }
  }

  /**
   * Expansion of RDTSC (called GET_TIME_BASE for consistency with PPC)
   *
   * @param s the instruction to expand
   * @param result the result/first operand
   */
  protected final void GET_TIME_BASE(Instruction s,
      RegisterOperand result) {
    Register highReg = result.getRegister();
    Register lowReg = regpool.getSecondReg(highReg);
    EMIT(CPOS(s, MIR_RDTSC.create(IA32_RDTSC,
        new RegisterOperand(getEAX(), TypeReference.Int),
        new RegisterOperand(getEDX(), TypeReference.Int))));
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
        new RegisterOperand(lowReg, TypeReference.Int),
        new RegisterOperand(getEAX(), TypeReference.Int))));
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV,
        new RegisterOperand(highReg, TypeReference.Int),
        new RegisterOperand(getEDX(), TypeReference.Int))));
  }

  /**
   * Expansion of LONG_CMP: compare to values and set result to -1, 0, 1 for <, =, >,
   * respectively
   *
   * @param s the compare instruction
   * @param res the result/first operand
   * @param val1 the first value
   * @param val2 the second value
   */
  protected final void LONG_CMP(Instruction s, RegisterOperand res, Operand val1, Operand val2) {
    RegisterOperand one = regpool.makeTempInt();
    RegisterOperand lone = regpool.makeTempInt();
    Operand two, ltwo;
    if (val1 instanceof RegisterOperand) {
      Register val1_reg = val1.asRegister().getRegister();
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, one, new RegisterOperand(val1_reg, TypeReference.Int))));
      EMIT(CPOS(s,
                MIR_Move.create(IA32_MOV,
                                lone,
                                new RegisterOperand(regpool.getSecondReg(val1_reg), TypeReference.Int))));
    } else {
      LongConstantOperand tmp = (LongConstantOperand) val1;
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, one, IC(tmp.upper32()))));
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, lone, IC(tmp.lower32()))));
    }
    if (val2 instanceof RegisterOperand) {
      two = val2;
      ltwo = L(burs.ir.regpool.getSecondReg(val2.asRegister().getRegister()));
    } else {
      LongConstantOperand tmp = (LongConstantOperand) val2;
      two = IC(tmp.upper32());
      ltwo = IC(tmp.lower32());
    }
    EMIT(CPOS(s, MIR_Compare.create(IA32_CMP, lone.copyRO(), ltwo)));
    EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB, one.copyRO(), two)));
    EMIT(CPOS(s, MIR_Set
        .create(IA32_SET__B, res, IA32ConditionOperand.LT()))); // res =
    // (val1 < val2) ? 1 :0
    EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_OR, one.copyRO(), lone.copyRO())));
    EMIT(CPOS(s,
              MIR_Set.create(IA32_SET__B,
                             lone.copyRO(),
                             IA32ConditionOperand.NE()))); // lone = (val1 != val2) ? 1 : 0
    EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NEG, res.copyRO()))); // res = (val1 <
    // val2) ? -1 :0
    EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_OR, res.copyRO(), lone.copyRO())));
    EMIT(MIR_Unary.mutate(s, IA32_MOVSX__B, res.copyRO(), res.copyRO()));
  }

  /**
   * Expansion of FP_ADD_ACC, FP_MUL_ACC, FP_SUB_ACC, and FP_DIV_ACC. Moves
   * first value into fp0, accumulates second value into fp0 using op, moves fp0
   * into result.
   *
   * @param s the instruction to expand
   * @param op the floating point op to use
   * @param result the result operand
   * @param val1 the first operand
   * @param val2 the second operand
   */
  protected final void FP_MOV_OP_MOV(Instruction s, Operator op, Operand result, Operand val1,
                                     Operand val2) {
    EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, D(getFPR(0)), val1)));
    EMIT(MIR_BinaryAcc.mutate(s, op, D(getFPR(0)), val2));
    EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, result, D(getFPR(0)))));
  }

  /**
   * Expansion of FP_REM
   *
   * @param s the instruction to expand
   * @param val1 the first operand
   * @param val2 the second operand
   */
  protected final void FP_REM(Instruction s, Operand val1, Operand val2) {
    EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, D(getFPR(1)), val2)));
    EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, D(getFPR(0)), val1)));
    EMIT(MIR_BinaryAcc.mutate(s, IA32_FPREM, D(getFPR(0)), D(getFPR(1))));
  }

  /**
   * Expansion for [DF]CMP[GL] compare to values and set result to -1, 0, 1 for <, =, >,
   * respectively
   *
   * @param s the compare instruction
   */
  protected final void threeValueFPCmp(Instruction s) {
    // IMPORTANT: FCOMI only sets 3 of the 6 bits in EFLAGS, so
    // we can't quite just translate the condition operand as if it
    // were an integer compare.
    // FCMOI sets ZF, PF, and CF as follows:
    // Compare Results ZF PF CF
    // left > right 0 0 0
    // left < right 0 0 1
    // left == right 1 0 0
    // UNORDERED 1 1 1
    RegisterOperand one = (RegisterOperand) Binary.getClearVal1(s);
    RegisterOperand two = (RegisterOperand) Binary.getClearVal2(s);
    RegisterOperand res = Binary.getClearResult(s);
    RegisterOperand temp = burs.ir.regpool.makeTempInt();
    Register FP0 = burs.ir.regpool.getPhysicalRegisterSet().getFPR(0);
    if ((s.operator == DOUBLE_CMPL) || (s.operator == FLOAT_CMPL)) {
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, temp, IC(0))));
      // Perform compare
      EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, new RegisterOperand(FP0, TypeReference.Int), one)));
      EMIT(CPOS(s, MIR_Compare.create(IA32_FCOMI, new RegisterOperand(FP0, TypeReference.Int), two)));
      // res = (value1 > value2) ? 1 : 0
      // temp = ((value1 < value2) || unordered) ? -1 : 0
      EMIT(CPOS(s, MIR_Set.create(IA32_SET__B, res, IA32ConditionOperand
          .LGT())));
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB, temp.copyRO(), temp.copyRO())));
    } else {
      RegisterOperand temp2 = burs.ir.regpool.makeTempInt();
      // Perform compare
      EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, new RegisterOperand(FP0, TypeReference.Int), one)));
      EMIT(CPOS(s, MIR_Compare.create(IA32_FCOMI, new RegisterOperand(FP0, TypeReference.Int), two)));
      // res = (value1 > value2) ? 1 : 0
      // temp2 = (value1 unordered value2) ? 1 : 0
      // temp = ((value1 unordered value2) ? 1 : 0) - 0 - CF
      // (i.e. temp = (value1 < value2) ? -1 : 0)
      EMIT(CPOS(s, MIR_Set.create(IA32_SET__B, temp, IA32ConditionOperand
          .PO())));
      EMIT(CPOS(s, MIR_Set.create(IA32_SET__B, res, IA32ConditionOperand
          .LGT())));
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, temp2, temp.copyRO())));
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB, temp.copyRO(), IC(0))));
      // Put result from temp2 in res
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_OR, res.copyRO(), temp2.copyRO())));
    }
    EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_OR, res.copyRO(), temp.copyRO())));
    EMIT(MIR_Unary.mutate(s, IA32_MOVSX__B, res.copyRO(), res.copyRO()));
  }

  /**
   * Expansion of BOOLEAN_CMP_INT
   *
   * @param s the instruction to copy position info from
   * @param res the result operand
   * @param val1 the first value
   * @param val2 the second value
   * @param cond the condition operand
   */
  protected final void BOOLEAN_CMP_INT(Instruction s, RegisterOperand res, Operand val1, Operand val2,
                                       ConditionOperand cond) {
    EMIT(CPOS(s, MIR_Compare.create(IA32_CMP, val1, val2)));
    RegisterOperand temp = regpool.makeTemp(TypeReference.Boolean);
    EMIT(CPOS(s, MIR_Set.create(IA32_SET__B, temp, COND(cond))));
    EMIT(MIR_Unary.mutate(s, IA32_MOVZX__B, res, temp.copyD2U()));
  }

  /**
   * Expansion of a special case of BOOLEAN_CMP_INT when the condition registers
   * have already been set by the previous ALU op.
   *
   * @param s the instruction to copy position info from
   * @param res the result operand
   * @param cond the condition operand
   */
  protected final void BOOLEAN_CMP_INT(Instruction s, RegisterOperand res, ConditionOperand cond) {
    RegisterOperand temp = regpool.makeTemp(TypeReference.Boolean);
    EMIT(CPOS(s, MIR_Set.create(IA32_SET__B, temp, COND(cond))));
    EMIT(MIR_Unary.mutate(s, IA32_MOVZX__B, res, temp.copyD2U()));
  }

  /**
   * Expansion of BOOLEAN_CMP_DOUBLE
   *
   * @param s the instruction to copy position info from
   * @param res the result operand
   * @param val1 the first value
   * @param val2 the second value
   * @param cond the condition operand
   */
  protected final void BOOLEAN_CMP_DOUBLE(Instruction s, RegisterOperand res, ConditionOperand cond,
                                          Operand val1, Operand val2) {
    RegisterOperand temp = regpool.makeTemp(TypeReference.Boolean);
    EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, D(getFPR(0)), CondMove.getVal1(s))));
    EMIT(CPOS(s, MIR_Compare.create(IA32_FCOMI, D(getFPR(0)), CondMove
        .getVal2(s))));
    EMIT(CPOS(s, MIR_Set.create(IA32_SET__B, temp, COND(cond))));
    EMIT(MIR_Unary.mutate(s, IA32_MOVZX__B, res, temp.copyD2U()));
  }

  /**
   * Expansion of BOOLEAN_CMP_LONG
   *
   * @param s the instruction to copy position info from
   * @param res the result operand
   * @param val1 the first value
   * @param val2 the second value
   * @param cond the condition operand
   */
  protected final void BOOLEAN_CMP_LONG(Instruction s, RegisterOperand res, Operand val1, Operand val2,
                                        ConditionOperand cond) {
    // Can we simplify to a shift?
    if (cond.isLESS() && val2.isLongConstant() && val2.asLongConstant().value == 0 && val1.isRegister()) {
      // Put the most significant bit of val1 into res
      Register val1_reg = val1.asRegister().getRegister();
      EMIT(MIR_Move.create(IA32_MOV, res.copyRO(), new RegisterOperand(val1_reg, TypeReference.Int)));
      EMIT(MIR_BinaryAcc.mutate(s, IA32_SHR, res, IC(31)));
    } else if (cond.isGREATER_EQUAL() && val2.isLongConstant() && val2.asLongConstant().value == 0 && val1.isRegister()) {
      // Put the most significant bit of val1 into res and invert
      Register val1_reg = val1.asRegister().getRegister();
      EMIT(MIR_Move.create(IA32_MOV, res.copyRO(), new RegisterOperand(val1_reg, TypeReference.Int)));
      EMIT(MIR_BinaryAcc.mutate(s, IA32_SHR, res, IC(31)));
      EMIT(MIR_BinaryAcc.create(IA32_XOR, res.copyRO(), IC(1)));
    } else {
      // Long comparison is a subtraction:
      // <, >= : easy to compute as SF !=/== OF
      // >, <= : flipOperands and treat as a </>=
      // ==/!= : do subtract then OR 2 32-bit quantities test for zero/non-zero
      if (cond.isGREATER() || cond.isLESS_EQUAL()) {
        Operand swap_temp;
        cond.flipOperands();
        swap_temp = val1;
        val1 = val2;
        val2 = swap_temp;
      }
      if (VM.VerifyAssertions) {
        VM._assert(cond.isEQUAL() || cond.isNOT_EQUAL() || cond.isLESS() || cond.isGREATER_EQUAL());
      }
      RegisterOperand one = regpool.makeTempInt();
      RegisterOperand lone = regpool.makeTempInt();
      Operand two, ltwo;
      if (val1 instanceof RegisterOperand) {
        Register val1_reg = val1.asRegister().getRegister();
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV, one, new RegisterOperand(val1_reg, TypeReference.Int))));
        EMIT(CPOS(s,
            MIR_Move.create(IA32_MOV,
                lone,
                new RegisterOperand(regpool.getSecondReg(val1_reg), TypeReference.Int))));
      } else {
        LongConstantOperand tmp = (LongConstantOperand) val1;
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV, one, IC(tmp.upper32()))));
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV, lone, IC(tmp.lower32()))));
      }
      if (val2 instanceof RegisterOperand) {
        two = val2;
        ltwo = L(burs.ir.regpool.getSecondReg(val2.asRegister().getRegister()));
      } else {
        LongConstantOperand tmp = (LongConstantOperand) val2;
        two = IC(tmp.upper32());
        ltwo = IC(tmp.lower32());
      }
      if (cond.isEQUAL() || cond.isNOT_EQUAL()) {
        EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB, lone.copyRO(), ltwo)));
        EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB, one.copyRO(), two)));
        EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_OR, one.copyRO(), lone.copyRO())));
      } else {
        EMIT(CPOS(s, MIR_Compare.create(IA32_CMP, lone.copyRO(), ltwo)));
        EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB, one.copyRO(), two)));
      }
      RegisterOperand temp = regpool.makeTemp(TypeReference.Boolean);
      EMIT(CPOS(s, MIR_Set.create(IA32_SET__B, temp, COND(cond))));
      EMIT(MIR_Unary.mutate(s, IA32_MOVZX__B, res, temp.copyRO()));
    }
  }

  /**
   * Generate a long compare and cmov
   *
   * @param s the instruction to copy position info from
   * @param result the result of the conditional move
   * @param val1 the first value
   * @param val2 the second value
   * @param cond the condition operand
   * @param trueValue the value to move to result if cond is true
   * @param falseValue the value to move to result if cond is not true
   */
  protected final void LCMP_CMOV(Instruction s, RegisterOperand result, Operand val1, Operand val2,
                                 ConditionOperand cond, Operand trueValue, Operand falseValue) {
    // Long comparison is a subtraction:
    // <, >= : easy to compute as SF !=/== OF
    // >, <= : flipOperands and treat as a </>=
    // ==/!= : do subtract then OR 2 32-bit quantities test for zero/non-zero
    if (cond.isGREATER() || cond.isLESS_EQUAL()) {
      Operand swap_temp;
      cond.flipOperands();
      swap_temp = val1;
      val1 = val2;
      val2 = swap_temp;
    }
    if (VM.VerifyAssertions) {
      VM._assert(cond.isEQUAL() || cond.isNOT_EQUAL() || cond.isLESS() || cond.isGREATER_EQUAL());
    }
    RegisterOperand one = regpool.makeTempInt();
    RegisterOperand lone = regpool.makeTempInt();
    Operand two, ltwo;
    if (val1 instanceof RegisterOperand) {
      Register val1_reg = val1.asRegister().getRegister();
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, one, new RegisterOperand(val1_reg, TypeReference.Int))));
      EMIT(CPOS(s,
                MIR_Move.create(IA32_MOV,
                                lone,
                                new RegisterOperand(regpool.getSecondReg(val1_reg), TypeReference.Int))));
    } else {
      LongConstantOperand tmp = (LongConstantOperand) val1;
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, one, IC(tmp.upper32()))));
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, lone, IC(tmp.lower32()))));
    }
    if (val2 instanceof RegisterOperand) {
      two = val2;
      ltwo = L(burs.ir.regpool.getSecondReg(val2.asRegister().getRegister()));
    } else {
      LongConstantOperand tmp = (LongConstantOperand) val2;
      two = IC(tmp.upper32());
      ltwo = IC(tmp.lower32());
    }
    if (cond.isEQUAL() || cond.isNOT_EQUAL()) {
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB, lone.copyRO(), ltwo)));
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB, one.copyRO(), two)));
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_OR, one.copyRO(), lone.copyRO())));
    } else {
      EMIT(CPOS(s, MIR_Compare.create(IA32_CMP, lone.copyRO(), ltwo)));
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SBB, one.copyRO(), two)));
    }
    CMOV_MOV(s, result, cond, trueValue, falseValue);
  }

  /**
   * Generate a compare and branch sequence. Used in the expansion of trees
   * where INT_IFCMP is a root
   *
   * @param s the ifcmp instruction
   * @param guardResult the guard result of the ifcmp
   * @param val1 the first value operand
   * @param val2 the second value operand
   * @param cond the condition operand
   */
  protected final void IFCMP(Instruction s, RegisterOperand guardResult, Operand val1, Operand val2,
                             ConditionOperand cond) {
    if (VM.VerifyAssertions) {
      // We only need make sure the guard information is correct when
      // validating, the null check combining phase removes all guards
      EMIT(CPOS(s, Move.create(GUARD_MOVE, guardResult, new TrueGuardOperand())));
    }
    EMIT(CPOS(s, MIR_Compare.create(IA32_CMP, val1, val2)));
    EMIT(MIR_CondBranch.mutate(s, IA32_JCC, COND(cond), IfCmp.getTarget(s), IfCmp.getBranchProfile(s)));
  }

  /**
   * Generate an integer move portion of a conditional move.
   *
   * @param s the instruction to copy position info from
   * @param result the result of the conditional move
   * @param cond the condition operand
   * @param trueValue the value to move to result if cond is true
   * @param falseValue the value to move to result if cond is not true
   */
  protected final void CMOV_MOV(Instruction s, RegisterOperand result, ConditionOperand cond,
                                Operand trueValue, Operand falseValue) {
    if (result.similar(trueValue)) {
      // in this case, only need a conditional move for the false branch.
      EMIT(MIR_CondMove.mutate(s, IA32_CMOV, result, asReg(s, IA32_MOV, falseValue), COND(cond.flipCode())));
    } else if (result.similar(falseValue)) {
      // in this case, only need a conditional move for the true branch.
      EMIT(MIR_CondMove.mutate(s, IA32_CMOV, result, asReg(s, IA32_MOV, trueValue), COND(cond)));
    } else {
      // need to handle both possible assignments. Unconditionally
      // assign one value then conditionally assign the other.
      if (falseValue.isRegister()) {
        EMIT(CPOS(s, MIR_Move.create(IA32_MOV, result, trueValue)));
        EMIT(MIR_CondMove.mutate(s, IA32_CMOV, result.copyRO(), falseValue, COND(cond.flipCode())));
      } else {
        if (trueValue.isRegister()) {
          EMIT(CPOS(s, MIR_Move.create(IA32_MOV, result, falseValue)));
          EMIT(MIR_CondMove.mutate(s, IA32_CMOV, result.copyRO(), trueValue, COND(cond)));
        } else {
          // Perform constant move without creating a register (costs
          // 1 or 2 more instructions but saves a register)
          int true_const = ((IntConstantOperand) trueValue).value;
          int false_const = ((IntConstantOperand) falseValue).value;
          // Generate values for consts trying to avoid zero extending the
          // set__b result
          // result = cond ? 1 : 0
          EMIT(CPOS(s, MIR_Set.create(IA32_SET__B, result.copyRO(), COND(cond))));

          if ((true_const - false_const) == 1) {
            // result = (cond ? 1 : 0) + false_const
            EMIT(CPOS(s, MIR_Unary.create(IA32_MOVZX__B, result.copyRO(), result.copyRO())));
            EMIT(MIR_BinaryAcc.mutate(s, IA32_ADD, result, IC(false_const)));
          } else if ((false_const - true_const) == 1) {
            // result = (cond ? -1 : 0) + false_const
            EMIT(CPOS(s, MIR_Unary.create(IA32_MOVZX__B, result.copyRO(), result.copyRO())));
            EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NEG, result.copyRO())));
            EMIT(MIR_BinaryAcc.mutate(s, IA32_ADD, result, IC(false_const)));
          } else if (((false_const - true_const) > 0) && ((false_const - true_const) <= 0xFF)) {
            // result = cond ? 0 : -1
            // result = (cond ? 0 : -1) & (false_const - true__const)
            // result = ((cond ? 0 : -1) & (false_const - true_const)) +
            // true_const
            EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_SUB, result.copyRO(), IC(1))));
            EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_AND, result.copyRO(), IC(false_const - true_const))));
            EMIT(MIR_BinaryAcc.mutate(s, IA32_ADD, result, IC(true_const)));
          } else {
            // result = cond ? -1 : 0
            // result = (cond ? -1 : 0) & (true_const - false_const)
            // result = ((cond ? -1 : 0) & (true_const - false_const)) +
            // false_const
            if (((true_const - false_const) > 0xFF) || ((true_const - false_const) < 0)) {
              EMIT(CPOS(s, MIR_Unary.create(IA32_MOVZX__B, result.copyRO(), result.copyRO())));
            }
            EMIT(CPOS(s, MIR_UnaryAcc.create(IA32_NEG, result.copyRO())));
            EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_AND, result.copyRO(), IC(true_const - false_const))));
            EMIT(MIR_BinaryAcc.mutate(s, IA32_ADD, result, IC(false_const)));
          }
        }
      }
    }
  }

  /**
   * Generate a floating point move portion of a conditional move.
   *
   * @param s the instruction to copy position info from
   * @param result the result of the conditional move
   * @param cond the condition operand
   * @param trueValue the value to move to result if cond is true
   * @param falseValue the value to move to result if cond is not true
   */
  protected final void CMOV_FMOV(Instruction s, RegisterOperand result, ConditionOperand cond,
                                 Operand trueValue, Operand falseValue) {
    if (result.similar(trueValue)) {
      // in this case, only need a conditional move for the false branch.
      EMIT(MIR_CondMove.mutate(s, IA32_FCMOV, result, asReg(s, IA32_FMOV, falseValue), COND(cond.flipCode())));
    } else if (result.similar(falseValue)) {
      // in this case, only need a conditional move for the true branch.
      EMIT(MIR_CondMove.mutate(s, IA32_FCMOV, result, asReg(s, IA32_FMOV, trueValue), COND(cond)));
    } else {
      // need to handle both possible assignments. Unconditionally
      // assign one value then conditionally assign the other.
      if (falseValue.isRegister()) {
        EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, result, trueValue)));
        EMIT(MIR_CondMove.mutate(s, IA32_FCMOV, result.copyRO(), falseValue, COND(cond.flipCode())));
      } else {
        EMIT(CPOS(s, MIR_Move.create(IA32_FMOV, result, falseValue)));
        EMIT(MIR_CondMove.mutate(s, IA32_FCMOV, result.copyRO(), asReg(s, IA32_FMOV, trueValue), COND(cond)));
      }
    }
  }

  /**
   * Expand a prologue by expanding out longs into pairs of ints
   */
  protected final void PROLOGUE(Instruction s) {
    int numFormals = Prologue.getNumberOfFormals(s);
    int numLongs = 0;
    for (int i = 0; i < numFormals; i++) {
      if (Prologue.getFormal(s, i).getType().isLongType()) {
        numLongs++;
      }
    }
    if (numLongs != 0) {
      Instruction s2 = Prologue.create(IR_PROLOGUE, numFormals + numLongs);
      for (int sidx = 0, s2idx = 0; sidx < numFormals; sidx++) {
        RegisterOperand sForm = Prologue.getFormal(s, sidx);
        if (sForm.getType().isLongType()) {
          sForm.setType(TypeReference.Int);
          Prologue.setFormal(s2, s2idx++, sForm);
          Register r2 = regpool.getSecondReg(sForm.getRegister());
          Prologue.setFormal(s2, s2idx++, new RegisterOperand(r2, TypeReference.Int));
          sForm.getRegister().clearType();
          sForm.getRegister().setInteger();
          r2.clearType();
          r2.setInteger();
        } else {
          Prologue.setFormal(s2, s2idx++, sForm);
        }
      }
      EMIT(s2);
    } else {
      EMIT(s);
    }
  }

  /**
   * Expansion of CALL. Expand longs registers into pairs of int registers.
   *
   * @param s the instruction to expand
   * @param address the operand containing the target address
   */
  protected final void CALL(Instruction s, Operand address) {
    // Step 1: Find out how many parameters we're going to have.
    int numParams = Call.getNumberOfParams(s);
    int longParams = 0;
    for (int pNum = 0; pNum < numParams; pNum++) {
      if (Call.getParam(s, pNum).getType().isLongType()) {
        longParams++;
      }
    }

    // Step 2: Figure out what the result and result2 values will be.
    RegisterOperand result = Call.getResult(s);
    RegisterOperand result2 = null;
    if (result != null && result.getType().isLongType()) {
      result.setType(TypeReference.Int);
      result2 = new RegisterOperand(regpool.getSecondReg(result.getRegister()), TypeReference.Int);
    }

    // Step 3: Mutate the Call to an MIR_Call.
    // Note MIR_Call and Call have a different number of fixed
    // arguments, so some amount of copying is required.
    Operand[] params = new Operand[numParams];
    for (int i = 0; i < numParams; i++) {
      params[i] = Call.getParam(s, i);
    }
    MIR_Call.mutate(s, IA32_CALL, result, result2, address, Call.getMethod(s), numParams + longParams);
    for (int paramIdx = 0, mirCallIdx = 0; paramIdx < numParams;) {
      Operand param = params[paramIdx++];
      if (param instanceof RegisterOperand) {
        MIR_Call.setParam(s, mirCallIdx++, param);
        RegisterOperand rparam = (RegisterOperand) param;
        if (rparam.getType().isLongType()) {
          MIR_Call.setParam(s, mirCallIdx++, L(regpool
              .getSecondReg(rparam.getRegister())));
        }
      } else if (param instanceof LongConstantOperand) {
        LongConstantOperand val = (LongConstantOperand) param;
        MIR_Call.setParam(s, mirCallIdx++, IC(val.upper32()));
        MIR_Call.setParam(s, mirCallIdx++, IC(val.lower32()));
      } else {
        MIR_Call.setParam(s, mirCallIdx++, param);
      }
    }

    // emit the call instruction.
    EMIT(s);
  }

  /**
   * Expansion of SYSCALL. Expand longs registers into pairs of int registers.
   *
   * @param s the instruction to expand
   * @param address the operand containing the target address
   */
  protected final void SYSCALL(Instruction s, Operand address) {
    burs.ir.setHasSysCall(true);

    // Step 1: Find out how many parameters we're going to have.
    int numParams = Call.getNumberOfParams(s);
    int longParams = 0;
    for (int pNum = 0; pNum < numParams; pNum++) {
      if (Call.getParam(s, pNum).getType().isLongType()) {
        longParams++;
      }
    }

    // Step 2: Figure out what the result and result2 values will be.
    RegisterOperand result = Call.getResult(s);
    RegisterOperand result2 = null;
    // NOTE: C callee returns longs little endian!
    if (result != null && result.getType().isLongType()) {
      result.setType(TypeReference.Int);
      result2 = result;
      result = new RegisterOperand(regpool.getSecondReg(result.getRegister()), TypeReference.Int);
    }

    // Step 3: Mutate the Call to an MIR_Call.
    // Note MIR_Call and Call have a different number of fixed
    // arguments, so some amount of copying is required.
    Operand[] params = new Operand[numParams];
    for (int i = 0; i < numParams; i++) {
      params[i] = Call.getParam(s, i);
    }
    MIR_Call.mutate(s, IA32_SYSCALL, result, result2, address, Call
        .getMethod(s), numParams + longParams);
    for (int paramIdx = 0, mirCallIdx = 0; paramIdx < numParams;) {
      Operand param = params[paramIdx++];
      if (param instanceof RegisterOperand) {
        // NOTE: longs passed little endian to C callee!
        RegisterOperand rparam = (RegisterOperand) param;
        if (rparam.getType().isLongType()) {
          MIR_Call.setParam(s, mirCallIdx++, L(regpool
              .getSecondReg(rparam.getRegister())));
        }
        MIR_Call.setParam(s, mirCallIdx++, param);
      } else if (param instanceof LongConstantOperand) {
        long value = ((LongConstantOperand) param).value;
        int valueHigh = (int) (value >> 32);
        int valueLow = (int) (value & 0xffffffff);
        // NOTE: longs passed little endian to C callee!
        MIR_Call.setParam(s, mirCallIdx++, IC(valueLow));
        MIR_Call.setParam(s, mirCallIdx++, IC(valueHigh));
      } else {
        MIR_Call.setParam(s, mirCallIdx++, param);
      }
    }

    // emit the call instruction.
    EMIT(s);
  }

  /**
   * Expansion of LOWTABLESWITCH.
   *
   * @param s the instruction to expand
   */
  protected final void LOWTABLESWITCH(Instruction s) {
    // (1) We're changing index from a U to a DU.
    // Inject a fresh copy instruction to make sure we aren't
    // going to get into trouble (if someone else was also using index).
    RegisterOperand newIndex = regpool.makeTempInt();
    EMIT(CPOS(s, MIR_Move.create(IA32_MOV, newIndex, LowTableSwitch.getIndex(s))));
    int number = LowTableSwitch.getNumberOfTargets(s);
    Instruction s2 = CPOS(s, MIR_LowTableSwitch.create(MIR_LOWTABLESWITCH, newIndex.copyRO(), number * 2));
    for (int i = 0; i < number; i++) {
      MIR_LowTableSwitch.setTarget(s2, i, LowTableSwitch.getTarget(s, i));
      MIR_LowTableSwitch.setBranchProfile(s2, i, LowTableSwitch
          .getBranchProfile(s, i));
    }
    EMIT(s2);
  }

  /**
   * Expansion of RESOLVE. Dynamic link point. Build up MIR instructions for
   * Resolve.
   *
   * @param s the instruction to expand
   */
  protected final void RESOLVE(Instruction s) {
    Operand target = loadFromJTOC(Entrypoints.optResolveMethod.getOffset(), DW);
    EMIT(CPOS(s,
              MIR_Call.mutate0(s,
                               CALL_SAVE_VOLATILE,
                               null,
                               null,
                               target,
                               MethodOperand.STATIC(Entrypoints.optResolveMethod))));
  }

  /**
   * Expansion of TRAP_IF, with an int constant as the second value.
   *
   * @param s the instruction to expand
   * @param longConstant is the argument a long constant?
   */
  protected final void TRAP_IF_IMM(Instruction s, boolean longConstant) {
    RegisterOperand gRes = TrapIf.getGuardResult(s);
    RegisterOperand v1 = (RegisterOperand) TrapIf.getVal1(s);
    ConstantOperand v2 = (ConstantOperand) TrapIf.getVal2(s);
    ConditionOperand cond = TrapIf.getCond(s);
    TrapCodeOperand tc = TrapIf.getTCode(s);

    // A slightly ugly matter, but we need to deal with combining
    // the two pieces of a long register from a LONG_ZERO_CHECK.
    // A little awkward, but probably the easiest workaround...
    if (longConstant) {
      if (VM.VerifyAssertions) {
        VM._assert((tc.getTrapCode() == RuntimeEntrypoints.TRAP_DIVIDE_BY_ZERO) &&
                   (((LongConstantOperand) v2).value == 0L));
      }
      RegisterOperand rr = regpool.makeTempInt();
      EMIT(CPOS(s, MIR_Move.create(IA32_MOV, rr, v1.copy())));
      EMIT(CPOS(s, MIR_BinaryAcc.create(IA32_OR,
                                rr.copy(),
                                new RegisterOperand(regpool.getSecondReg(v1.getRegister()), TypeReference.Int))));
      v1 = rr.copyD2U();
      v2 = IC(0);
    }
    // emit the trap instruction
    EMIT(MIR_TrapIf.mutate(s, IA32_TRAPIF, gRes, v1, v2, COND(cond), tc));
  }

  /**
   * This routine expands an ATTEMPT instruction into an atomic
   * compare exchange. The atomic compare and exchange will place at
   * mo the value of newValue if the value of mo is oldValue. The
   * result register is set to 0/1 depending on whether the valye was
   * replaced or not.
   *
   * @param result the register operand that is set to 0/1 as a result of the
   *          attempt
   * @param mo the address at which to attempt the exchange
   * @param oldValue the old value at the address mo
   * @param newValue the new value at the address mo
   */
  protected final void ATTEMPT(RegisterOperand result, MemoryOperand mo, Operand oldValue,
                               Operand newValue) {
    RegisterOperand temp = regpool.makeTempInt();
    RegisterOperand temp2 = regpool.makeTemp(result);
    EMIT(MIR_Move.create(IA32_MOV, temp, newValue));
    EMIT(MIR_Move.create(IA32_MOV, new RegisterOperand(getEAX(), TypeReference.Int), oldValue));
    EMIT(MIR_CompareExchange.create(IA32_LOCK_CMPXCHG,
                                    new RegisterOperand(getEAX(), TypeReference.Int),
                                    mo,
                                    temp.copyRO()));
    EMIT(MIR_Set.create(IA32_SET__B, temp2, IA32ConditionOperand.EQ()));
    // need to zero-extend the result of the set
    EMIT(MIR_Unary.create(IA32_MOVZX__B, result, temp2.copy()));
  }

  /**
   * This routine expands an ATTEMPT instruction into an atomic
   * compare exchange. The atomic compare and exchange will place at
   * mo the value of newValue if the value of mo is oldValue. The
   * result register is set to 0/1 depending on whether the valye was
   * replaced or not.
   *
   * @param result the register operand that is set to 0/1 as a result
   * of the attempt
   * @param mo       the address at which to attempt the exchange
   * @param oldValue the old value to check for at the address mo
   * @param newValue the new value to place at the address mo
   */
  protected final void ATTEMPT_LONG(RegisterOperand result,
                                    MemoryOperand mo,
                                    Operand oldValue,
                                    Operand newValue) {
    // Set up EDX:EAX with the old value
    if (oldValue.isRegister()) {
      Register oldValue_hval = oldValue.asRegister().getRegister();
      Register oldValue_lval = regpool.getSecondReg(oldValue_hval);
      EMIT(MIR_Move.create(IA32_MOV, new RegisterOperand(getEDX(), TypeReference.Int),
          new RegisterOperand(oldValue_hval, TypeReference.Int)));
      EMIT(MIR_Move.create(IA32_MOV, new RegisterOperand(getEAX(), TypeReference.Int),
          new RegisterOperand(oldValue_lval, TypeReference.Int)));
    } else {
      if (VM.VerifyAssertions) VM._assert(oldValue.isLongConstant());
      LongConstantOperand val = oldValue.asLongConstant();
      EMIT(MIR_Move.create(IA32_MOV, new RegisterOperand(getEDX(), TypeReference.Int),
          IC(val.upper32())));
      EMIT(MIR_Move.create(IA32_MOV, new RegisterOperand(getEAX(), TypeReference.Int),
          IC(val.lower32())));
    }

    // Set up ECX:EBX with the new value
    if (newValue.isRegister()) {
      Register newValue_hval = newValue.asRegister().getRegister();
      Register newValue_lval = regpool.getSecondReg(newValue_hval);
      EMIT(MIR_Move.create(IA32_MOV, new RegisterOperand(getECX(), TypeReference.Int),
          new RegisterOperand(newValue_hval, TypeReference.Int)));
      EMIT(MIR_Move.create(IA32_MOV, new RegisterOperand(getEBX(), TypeReference.Int),
          new RegisterOperand(newValue_lval, TypeReference.Int)));
    } else {
      if (VM.VerifyAssertions) VM._assert(newValue.isLongConstant());
      LongConstantOperand val = newValue.asLongConstant();
      EMIT(MIR_Move.create(IA32_MOV, new RegisterOperand(getECX(), TypeReference.Int),
          IC(val.upper32())));
      EMIT(MIR_Move.create(IA32_MOV, new RegisterOperand(getEBX(), TypeReference.Int),
          IC(val.lower32())));
    }

    EMIT(MIR_CompareExchange8B.create(IA32_LOCK_CMPXCHG8B,
         new RegisterOperand(getEDX(), TypeReference.Int),
         new RegisterOperand(getEAX(), TypeReference.Int),
         mo,
         new RegisterOperand(getECX(), TypeReference.Int),
         new RegisterOperand(getEBX(), TypeReference.Int)));

    RegisterOperand temp = regpool.makeTemp(result);
    EMIT(MIR_Set.create(IA32_SET__B, temp, IA32ConditionOperand.EQ()));
    // need to zero-extend the result of the set
    EMIT(MIR_Unary.create(IA32_MOVZX__B, result, temp.copy()));
  }

  /**
   * This routine expands the compound pattern IFCMP(ATTEMPT, ZERO) into an
   * atomic compare/exchange followed by a branch on success/failure of the
   * attempted atomic compare/exchange.
   *
   * @param mo the address at which to attempt the exchange
   * @param oldValue the old value at the address mo
   * @param newValue the new value at the address mo
   * @param cond the condition to branch on
   * @param target the branch target
   * @param bp the branch profile information
   */
  protected final void ATTEMPT_IFCMP(MemoryOperand mo, Operand oldValue, Operand newValue,
                                     ConditionOperand cond, BranchOperand target, BranchProfileOperand bp) {
    RegisterOperand temp = regpool.makeTempInt();
    EMIT(MIR_Move.create(IA32_MOV, temp, newValue));
    EMIT(MIR_Move.create(IA32_MOV, new RegisterOperand(getEAX(), TypeReference.Int), oldValue));
    EMIT(MIR_CompareExchange.create(IA32_LOCK_CMPXCHG,
                                    new RegisterOperand(getEAX(), TypeReference.Int),
                                    mo,
                                    temp.copyRO()));
    EMIT(MIR_CondBranch.create(IA32_JCC, COND(cond), target, bp));
  }

  /*
   * special case handling OSR instructions expand long type variables to two
   * intergers
   */
  void OSR(BURS burs, Instruction s) {
    if (VM.VerifyAssertions) {
      VM._assert(OsrPoint.conforms(s));
    }

    // 1. how many params
    int numparam = OsrPoint.getNumberOfElements(s);
    int numlong = 0;
    for (int i = 0; i < numparam; i++) {
      Operand param = OsrPoint.getElement(s, i);
      if (param.getType().isLongType()) {
        numlong++;
      }
    }

    // 2. collect params
    InlinedOsrTypeInfoOperand typeInfo = OsrPoint
        .getClearInlinedTypeInfo(s);

    if (VM.VerifyAssertions) {
      if (typeInfo == null) {
        VM.sysWriteln("OsrPoint " + s + " has a <null> type info:");
        VM.sysWriteln("  position :" + s.bcIndex + "@" + s.position.method);
      }
      VM._assert(typeInfo != null);
    }

    Operand[] params = new Operand[numparam];
    for (int i = 0; i < numparam; i++) {
      params[i] = OsrPoint.getClearElement(s, i);
    }

    // set the number of valid params in osr type info, used
    // in LinearScan
    typeInfo.validOps = numparam;

    // 3: only makes second half register of long being used
    // creates room for long types.
    burs.append(OsrPoint.mutate(s, s.operator(), typeInfo, numparam + numlong));

    int pidx = numparam;
    for (int i = 0; i < numparam; i++) {
      Operand param = params[i];
      OsrPoint.setElement(s, i, param);
      if (param instanceof RegisterOperand) {
        RegisterOperand rparam = (RegisterOperand) param;
        // the second half is appended at the end
        // LinearScan will update the map.
        if (rparam.getType().isLongType()) {
          OsrPoint.setElement(s, pidx++, L(burs.ir.regpool
              .getSecondReg(rparam.getRegister())));
        }
      } else if (param instanceof LongConstantOperand) {
        LongConstantOperand val = (LongConstantOperand) param;

        if (VM.TraceOnStackReplacement) {
          VM.sysWriteln("caught a long const " + val);
        }

        OsrPoint.setElement(s, i, IC(val.upper32()));
        OsrPoint.setElement(s, pidx++, IC(val.lower32()));
      } else if (param instanceof IntConstantOperand) {
      } else {
        throw new OptimizingCompilerException("BURS_Helpers", "unexpected parameter type" + param);
      }
    }

    if (pidx != (numparam + numlong)) {
      VM.sysWriteln("pidx = " + pidx);
      VM.sysWriteln("numparam = " + numparam);
      VM.sysWriteln("numlong = " + numlong);
    }

    if (VM.VerifyAssertions) {
      VM._assert(pidx == (numparam + numlong));
    }

    /*
     * if (VM.TraceOnStackReplacement) { VM.sysWriteln("BURS rewrite OsrPoint
     * "+s); VM.sysWriteln(" position "+s.bcIndex+"@"+s.position.method); }
     */
  }
}
