/*
 *  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.bc2ir;

import static org.jikesrvm.SizeConstants.LOG_BYTES_IN_ADDRESS;
import static org.jikesrvm.SizeConstants.LOG_BYTES_IN_INT;
import static org.jikesrvm.compilers.opt.ir.Operators.ADDR_2INT;
import static org.jikesrvm.compilers.opt.ir.Operators.ADDR_2LONG;
import static org.jikesrvm.compilers.opt.ir.Operators.ARRAYLENGTH;
import static org.jikesrvm.compilers.opt.ir.Operators.ATTEMPT_ADDR;
import static org.jikesrvm.compilers.opt.ir.Operators.ATTEMPT_INT;
import static org.jikesrvm.compilers.opt.ir.Operators.ATTEMPT_LONG;
import static org.jikesrvm.compilers.opt.ir.Operators.BOOLEAN_CMP_ADDR;
import static org.jikesrvm.compilers.opt.ir.Operators.BYTE_LOAD;
import static org.jikesrvm.compilers.opt.ir.Operators.BYTE_STORE;
import static org.jikesrvm.compilers.opt.ir.Operators.CALL;
import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_AS_LONG_BITS;
import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_LOAD;
import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_SQRT;
import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_STORE;
import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_AS_INT_BITS;
import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_LOAD;
import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_SQRT;
import static org.jikesrvm.compilers.opt.ir.Operators.FLOAT_STORE;
import static org.jikesrvm.compilers.opt.ir.Operators.GET_OBJ_TIB;
import static org.jikesrvm.compilers.opt.ir.Operators.GET_TIME_BASE;
import static org.jikesrvm.compilers.opt.ir.Operators.GET_TYPE_FROM_TIB;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_2ADDRSigExt;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_2ADDRZerExt;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_ADD;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_BITS_AS_FLOAT;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_LOAD;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_SHL;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_STORE;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_2ADDR;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_BITS_AS_DOUBLE;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_LOAD;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_STORE;
import static org.jikesrvm.compilers.opt.ir.Operators.PREPARE_ADDR;
import static org.jikesrvm.compilers.opt.ir.Operators.PREPARE_INT;
import static org.jikesrvm.compilers.opt.ir.Operators.PREPARE_LONG;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_ADD;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_AND;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_LOAD;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_NOT;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_OR;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_SHL;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_SHR;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_STORE;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_SUB;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_USHR;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_XOR;
import static org.jikesrvm.compilers.opt.ir.Operators.SHORT_LOAD;
import static org.jikesrvm.compilers.opt.ir.Operators.SHORT_STORE;
import static org.jikesrvm.compilers.opt.ir.Operators.SYSCALL;
import static org.jikesrvm.compilers.opt.ir.Operators.UBYTE_LOAD;
import static org.jikesrvm.compilers.opt.ir.Operators.USHORT_LOAD;

import org.jikesrvm.VM;
import org.jikesrvm.ArchitectureSpecificOpt.GenerateMachineSpecificMagic;
import org.jikesrvm.classloader.Atom;
import org.jikesrvm.classloader.RVMField;
import org.jikesrvm.classloader.MemberReference;
import org.jikesrvm.classloader.MethodReference;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.compilers.opt.MagicNotImplementedException;
import org.jikesrvm.compilers.opt.ir.Attempt;
import org.jikesrvm.compilers.opt.ir.Binary;
import org.jikesrvm.compilers.opt.ir.BooleanCmp;
import org.jikesrvm.compilers.opt.ir.Call;
import org.jikesrvm.compilers.opt.ir.GuardedUnary;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.Load;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.Nullary;
import org.jikesrvm.compilers.opt.ir.Operator;
import org.jikesrvm.compilers.opt.ir.Prepare;
import org.jikesrvm.compilers.opt.ir.Store;
import org.jikesrvm.compilers.opt.ir.Unary;
import org.jikesrvm.compilers.opt.ir.operand.AddressConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.BranchProfileOperand;
import org.jikesrvm.compilers.opt.ir.operand.ConditionOperand;
import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.LocationOperand;
import org.jikesrvm.compilers.opt.ir.operand.MethodOperand;
import org.jikesrvm.compilers.opt.ir.operand.ObjectConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
import org.jikesrvm.compilers.opt.ir.operand.TrueGuardOperand;
import org.jikesrvm.objectmodel.TIBLayoutConstants;
import org.jikesrvm.runtime.ArchEntrypoints;
import org.jikesrvm.runtime.MagicNames;
import org.jikesrvm.scheduler.Scheduler;
import org.jikesrvm.scheduler.greenthreads.GreenProcessor;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;

/**
 * This class implements the non-machine-specific magics for the opt compiler.
 * By non-machine-specific we mean that the IR generated to implement the magic
 * is independent of the target-architecture.
 * It does not mean that the eventual MIR that implements the magic
 * won't differ from architecture to architecture.
 */
public class GenerateMagic implements TIBLayoutConstants  {

  /**
   * "Semantic inlining" of methods of the Magic class.
   * Based on the methodName, generate a sequence of opt instructions
   * that implement the magic, updating the expression stack as necessary.
   *
   * @param bc2ir the bc2ir object that is generating the
   *              ir containing this magic
   * @param gc must be bc2ir.gc
   * @param meth the RVMMethod that is the magic method
   */
  static boolean generateMagic(BC2IR bc2ir, GenerationContext gc, MethodReference meth)
      throws MagicNotImplementedException {

    if (gc.method.hasNoInlinePragma()) gc.allocFrame = true;

    // HACK: Don't schedule any bbs containing unsafe magics.
    // TODO: move this to individual magics that are unsafe.
    // -- igor 08/13/1999
    bc2ir.markBBUnsafeForScheduling();
    Atom methodName = meth.getName();

    boolean address = (meth.getType() == TypeReference.Address);

    // Address magic
    TypeReference[] types = meth.getParameterTypes();
    TypeReference returnType = meth.getReturnType();

    if (address && isLoad(methodName)) {
      // LOAD
      Operand offset = (types.length == 0) ? new AddressConstantOperand(Address.zero()) : bc2ir.popAddress();
      Operand base = bc2ir.popAddress();
      RegisterOperand result = gc.temps.makeTemp(returnType);
      bc2ir.appendInstruction(Load.create(getOperator(returnType, LOAD_OP), result, base, offset, null));
      bc2ir.push(result.copyD2U(), returnType);

    } else if (address && isPrepare(methodName)) {
      // PREPARE
      Operand offset = (types.length == 0) ? new AddressConstantOperand(Address.zero()) : bc2ir.popAddress();
      Operand base = bc2ir.popAddress();
      RegisterOperand result = gc.temps.makeTemp(returnType);
      bc2ir.appendInstruction(Prepare.create(getOperator(returnType, PREPARE_OP), result, base, offset, null));
      bc2ir.push(result.copyD2U(), returnType);

    } else if (address && methodName == MagicNames.attempt) {
      // ATTEMPT
      TypeReference attemptType = types[0];

      Operand offset = (types.length == 2) ? new AddressConstantOperand(Address.zero()) : bc2ir.popAddress();

      Operand newVal = bc2ir.pop();
      Operand oldVal = bc2ir.pop();
      Operand base = bc2ir.popAddress();
      RegisterOperand test = gc.temps.makeTempInt();
      bc2ir.appendInstruction(Attempt.create(getOperator(attemptType, ATTEMPT_OP),
                                             test,
                                             base,
                                             offset,
                                             oldVal,
                                             newVal,
                                             null));
      bc2ir.push(test.copyD2U(), returnType);

    } else if (address && methodName == MagicNames.store) {
      // STORE
      TypeReference storeType = types[0];

      Operand offset = (types.length == 1) ? new AddressConstantOperand(Address.zero()) : bc2ir.popAddress();

      Operand val = bc2ir.pop(storeType);
      Operand base = bc2ir.popAddress();
      bc2ir.appendInstruction(Store.create(getOperator(storeType, STORE_OP), val, base, offset, null));

    } else if (methodName == MagicNames.getProcessorRegister) {
      RegisterOperand rop = gc.temps.makePROp();
      bc2ir.markGuardlessNonNull(rop);
      bc2ir.push(rop);
    } else if (methodName == MagicNames.setProcessorRegister) {
      Operand val = bc2ir.popRef();
      if (val instanceof RegisterOperand) {
        bc2ir.appendInstruction(Move.create(REF_MOVE, gc.temps.makePROp(), val));
      } else {
        String msg = " Unexpected operand Magic.setProcessorRegister";
        throw MagicNotImplementedException.UNEXPECTED(msg);
      }
    } else if (methodName == MagicNames.addressArrayCreate) {
      Instruction s = bc2ir.generateAnewarray(null, meth.getType().getArrayElementType());
      bc2ir.appendInstruction(s);
    } else if (methodName == MagicNames.addressArrayLength) {
      Operand op1 = bc2ir.pop();
      bc2ir.clearCurrentGuard();
      if (bc2ir.do_NullCheck(op1)) {
        return true;
      }
      RegisterOperand t = gc.temps.makeTempInt();
      Instruction s = GuardedUnary.create(ARRAYLENGTH, t, op1, bc2ir.getCurrentGuard());
      bc2ir.push(t.copyD2U());
      bc2ir.appendInstruction(s);
    } else if (methodName == MagicNames.addressArrayGet) {
      TypeReference elementType = meth.getReturnType();
      if (meth.getType() == TypeReference.ProcessorTable) {
        elementType = Scheduler.getProcessorType();
      }
      Operand index = bc2ir.popInt();
      Operand ref = bc2ir.popRef();
      RegisterOperand offsetI = gc.temps.makeTempInt();
      RegisterOperand offset = gc.temps.makeTempOffset();
      RegisterOperand result;
      if (meth.getType().isCodeArrayType()) {
        if (VM.BuildForIA32) {
          result = gc.temps.makeTemp(TypeReference.Byte);
          bc2ir.appendInstruction(Load.create(BYTE_LOAD,
                                              result,
                                              ref,
                                              index,
                                              new LocationOperand(elementType),
                                              new TrueGuardOperand()));
        } else if (VM.BuildForPowerPC) {
          result = gc.temps.makeTemp(TypeReference.Int);
          bc2ir.appendInstruction(Binary.create(INT_SHL, offsetI, index, new IntConstantOperand(LOG_BYTES_IN_INT)));
          bc2ir.appendInstruction(Unary.create(INT_2ADDRZerExt, offset, offsetI.copy()));
          bc2ir.appendInstruction(Load.create(INT_LOAD,
                                              result,
                                              ref,
                                              offset.copy(),
                                              new LocationOperand(elementType),
                                              new TrueGuardOperand()));
        }
      } else {
        result = gc.temps.makeTemp(elementType);
        bc2ir.appendInstruction(Binary.create(INT_SHL,
                                              offsetI,
                                              index,
                                              new IntConstantOperand(LOG_BYTES_IN_ADDRESS)));
        bc2ir.appendInstruction(Unary.create(INT_2ADDRZerExt, offset, offsetI.copy()));
        bc2ir.appendInstruction(Load.create(REF_LOAD,
                                            result,
                                            ref,
                                            offset.copy(),
                                            new LocationOperand(elementType),
                                            new TrueGuardOperand()));
      }
      bc2ir.push(result.copyD2U());
    } else if (methodName == MagicNames.addressArraySet) {
      TypeReference elementType = meth.getParameterTypes()[1];
      Operand val = bc2ir.pop();
      Operand index = bc2ir.popInt();
      Operand ref = bc2ir.popRef();
      RegisterOperand offsetI = gc.temps.makeTempInt();
      RegisterOperand offset = gc.temps.makeTempOffset();
      if (meth.getType().isCodeArrayType()) {
        if (VM.BuildForIA32) {
          bc2ir.appendInstruction(Store.create(BYTE_STORE,
                                               val,
                                               ref,
                                               index,
                                               new LocationOperand(elementType),
                                               new TrueGuardOperand()));
        } else if (VM.BuildForPowerPC) {
          bc2ir.appendInstruction(Binary.create(INT_SHL, offsetI, index, new IntConstantOperand(LOG_BYTES_IN_INT)));
          bc2ir.appendInstruction(Unary.create(INT_2ADDRZerExt, offset, offsetI.copy()));
          bc2ir.appendInstruction(Store.create(INT_STORE,
                                               val,
                                               ref,
                                               offset.copy(),
                                               new LocationOperand(elementType),
                                               new TrueGuardOperand()));
        }
      } else {
        bc2ir.appendInstruction(Binary.create(INT_SHL,
                                              offsetI,
                                              index,
                                              new IntConstantOperand(LOG_BYTES_IN_ADDRESS)));
        bc2ir.appendInstruction(Unary.create(INT_2ADDRZerExt, offset, offsetI.copy()));
        bc2ir.appendInstruction(Store.create(REF_STORE,
                                             val,
                                             ref,
                                             offset.copy(),
                                             new LocationOperand(elementType),
                                             new TrueGuardOperand()));
      }
    } else if (methodName == MagicNames.getIntAtOffset) {
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTempInt();
      bc2ir.appendInstruction(Load.create(INT_LOAD, val, object, offset, null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.setIntAtOffset) {
      Operand val = bc2ir.popInt();
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      bc2ir.appendInstruction(Store.create(INT_STORE, val, object, offset, null));
    } else if (methodName == MagicNames.getWordAtOffset) {
      LocationOperand loc = null;
      if (meth.getParameterTypes().length == 3) {
        loc = mapToMetadata(bc2ir.popInt());
      }
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.Word);
      bc2ir.appendInstruction(Load.create(REF_LOAD, val, object, offset, loc));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.setWordAtOffset) {
      LocationOperand loc = null;
      if (meth.getParameterTypes().length == 4) {
        loc = mapToMetadata(bc2ir.popInt());
      }
      Operand val = bc2ir.popRef();
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      bc2ir.appendInstruction(Store.create(REF_STORE, val, object, offset, loc));
    } else if (methodName == MagicNames.getLongAtOffset) {
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTempLong();
      bc2ir.appendInstruction(Load.create(LONG_LOAD, val, object, offset, null));
      bc2ir.pushDual(val.copyD2U());
    } else if (methodName == MagicNames.setLongAtOffset) {
      Operand val = bc2ir.popLong();
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      bc2ir.appendInstruction(Store.create(LONG_STORE, val, object, offset, null));
    } else if (methodName == MagicNames.getDoubleAtOffset) {
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTempDouble();
      bc2ir.appendInstruction(Load.create(DOUBLE_LOAD, val, object, offset, null));
      bc2ir.pushDual(val.copyD2U());
    } else if (methodName == MagicNames.setDoubleAtOffset) {
      Operand val = bc2ir.popDouble();
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      bc2ir.appendInstruction(Store.create(DOUBLE_STORE, val, object, offset, null));
    } else if (methodName == MagicNames.getObjectAtOffset) {
      LocationOperand loc = null;
      if (meth.getParameterTypes().length == 3) {
        loc = mapToMetadata(bc2ir.popInt());
      }
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.JavaLangObject);
      bc2ir.appendInstruction(Load.create(REF_LOAD, val, object, offset, loc));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.getTIBAtOffset) {
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.TIB);
      bc2ir.appendInstruction(Load.create(REF_LOAD, val, object, offset, null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.setObjectAtOffset) {
      LocationOperand loc = null;
      if (meth.getParameterTypes().length == 4) {
        loc = mapToMetadata(bc2ir.popInt());
      }
      Operand val = bc2ir.popRef();
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      bc2ir.appendInstruction(Store.create(REF_STORE, val, object, offset, loc));
    } else if (methodName == MagicNames.getByteAtOffset) {
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.Byte);
      bc2ir.appendInstruction(Load.create(BYTE_LOAD, val, object, offset, null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.getUnsignedByteAtOffset) {
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.Byte);
      bc2ir.appendInstruction(Load.create(UBYTE_LOAD, val, object, offset, null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.setByteAtOffset) {
      Operand val = bc2ir.popInt();
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      bc2ir.appendInstruction(Store.create(BYTE_STORE, val, object, offset, null));
    } else if (methodName == MagicNames.getShortAtOffset) {
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.Char);
      bc2ir.appendInstruction(Load.create(SHORT_LOAD, val, object, offset, null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.getCharAtOffset) {
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.Char);
      bc2ir.appendInstruction(Load.create(USHORT_LOAD, val, object, offset, null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.setCharAtOffset) {
      Operand val = bc2ir.popInt();
      Operand offset = bc2ir.popAddress();
      Operand object = bc2ir.popRef();
      bc2ir.appendInstruction(Store.create(SHORT_STORE, val, object, offset, null));
    } else if (methodName == MagicNames.getMemoryInt) {
      Operand memAddr = bc2ir.popAddress();
      RegisterOperand val = gc.temps.makeTempInt();
      bc2ir.appendInstruction(Load.create(INT_LOAD, val, memAddr, new AddressConstantOperand(Offset.zero()), null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.getMemoryWord) {
      Operand memAddr = bc2ir.popAddress();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.Word);
      bc2ir.appendInstruction(Load.create(REF_LOAD, val, memAddr, new AddressConstantOperand(Offset.zero()), null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.getMemoryAddress) {
      Operand memAddr = bc2ir.popAddress();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.Address);
      bc2ir.appendInstruction(Load.create(REF_LOAD, val, memAddr, new AddressConstantOperand(Offset.zero()), null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.setMemoryInt) {
      Operand val = bc2ir.popInt();
      Operand memAddr = bc2ir.popAddress();
      bc2ir.appendInstruction(Store.create(INT_STORE,
                                           val,
                                           memAddr,
                                           new AddressConstantOperand(Offset.zero()),
                                           null));
    } else if (methodName == MagicNames.setMemoryWord) {
      Operand val = bc2ir.popRef();
      Operand memAddr = bc2ir.popAddress();
      bc2ir.appendInstruction(Store.create(REF_STORE,
                                           val,
                                           memAddr,
                                           new AddressConstantOperand(Offset.zero()),
                                           null));
    } else if (meth.isSysCall()) {
      // All methods of SysCall have the following signature:
      // callNAME(Address functionAddress, <var args to pass via native calling convention>)
      // With POWEROPEN_ABI, functionAddress points to the function descriptor
      TypeReference[] args = meth.getParameterTypes();
      Instruction call = Call.create(SYSCALL, null, null, null, null, args.length - 1);
      for (int i = args.length - 1; i >= 1; i--) {
        Call.setParam(call, i - 1, bc2ir.pop(args[i]));
      }
      Operand functionAddress = bc2ir.pop(args[0]);
      Call.setAddress(call, functionAddress);
      if (!returnType.isVoidType()) {
        RegisterOperand op0 = gc.temps.makeTemp(returnType);
        Call.setResult(call, op0);
        bc2ir.push(op0.copyD2U(), returnType);
      }
      Call.setMethod(call, MethodOperand.STATIC(meth, meth.peekResolvedMethod()));
      bc2ir.appendInstruction(call);
    } else if (meth.isSpecializedInvoke()) {
      // The callsite looks like              RETURN = INVOKE (ID, OBJECT, P0, P1 .. PN)
      // And the actual method will look like RETURN = INVOKE     (OBJECT, P0, P1 .. PN)

      // Create the call instruction
      Instruction call = Call.create(CALL, null, null, null, null, types.length - 1);

      // Plumb all of the normal parameters into the call
      for (int i = types.length - 1; i >= 2; i--) {
        Call.setParam(call, i - 1, bc2ir.pop(types[i]));
      }
      // The object being specialized
      Operand objectOperand = bc2ir.pop(types[1]);
      Call.setParam(call, 0, objectOperand);
      Operand guard = BC2IR.getGuard(objectOperand);
      if (guard == null) {
        // it's magic, so assume that it's OK....
        guard = new TrueGuardOperand();
      }
      Call.setGuard(call, guard);

      // Load the tib of this object
      RegisterOperand tibObject = gc.temps.makeTemp(TypeReference.TIB);
      bc2ir.appendInstruction(GuardedUnary.create(GET_OBJ_TIB, tibObject, objectOperand.copy(), guard.copy()));

      // The index of the specialized method
      Operand methodId = bc2ir.popInt();

      // Add the base offset for specialized methods and convert from index to address
      RegisterOperand tibOffset = gc.temps.makeTemp(TypeReference.Int);
      bc2ir.appendInstruction(Binary.create(INT_ADD, tibOffset, methodId, new IntConstantOperand(TIB_FIRST_SPECIALIZED_METHOD_INDEX)));
      bc2ir.appendInstruction(Binary.create(INT_SHL, tibOffset.copyRO(), tibOffset.copyD2U(), new IntConstantOperand(LOG_BYTES_IN_ADDRESS)));

      // Load the code address from the TIB
      RegisterOperand codeAddress = gc.temps.makeTemp(TypeReference.Address);
      bc2ir.appendInstruction(Load.create(REF_LOAD, codeAddress, tibObject.copyD2U(), tibOffset.copyD2U(), null));

      Call.setAddress(call, codeAddress.copyD2U());
      if (!returnType.isVoidType()) {
        RegisterOperand op0 = gc.temps.makeTemp(returnType);
        Call.setResult(call, op0);
        bc2ir.push(op0.copyD2U(), returnType);
      }
      bc2ir.appendInstruction(call);
    } else if (methodName == MagicNames.threadAsCollectorThread) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.CollectorThread);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.objectAsType) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.Type);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.objectAsProcessor) {
      RegisterOperand reg = gc.temps.makeTemp(Scheduler.getProcessorType());
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.processorAsGreenProcessor) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.findOrCreate(GreenProcessor.class));
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.objectAsThread) {
      RegisterOperand reg = gc.temps.makeTemp(Scheduler.getThreadType());
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.objectAsAddress) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.Address);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.addressAsObject) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.JavaLangObject);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popAddress()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.addressAsTIB) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.TIB);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popAddress()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.addressAsByteArray) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.ByteArray);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popAddress()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.objectAsShortArray) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.ShortArray);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.objectAsIntArray) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.IntArray);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.floatAsIntBits) {
      Operand val = bc2ir.popFloat();
      RegisterOperand op0 = gc.temps.makeTempInt();
      bc2ir.appendInstruction(Unary.create(FLOAT_AS_INT_BITS, op0, val));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.intBitsAsFloat) {
      Operand val = bc2ir.popInt();
      RegisterOperand op0 = gc.temps.makeTempFloat();
      bc2ir.appendInstruction(Unary.create(INT_BITS_AS_FLOAT, op0, val));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.doubleAsLongBits) {
      Operand val = bc2ir.popDouble();
      RegisterOperand op0 = gc.temps.makeTempLong();
      bc2ir.appendInstruction(Unary.create(DOUBLE_AS_LONG_BITS, op0, val));
      bc2ir.pushDual(op0.copyD2U());
    } else if (methodName == MagicNames.longBitsAsDouble) {
      Operand val = bc2ir.popLong();
      RegisterOperand op0 = gc.temps.makeTempDouble();
      bc2ir.appendInstruction(Unary.create(LONG_BITS_AS_DOUBLE, op0, val));
      bc2ir.pushDual(op0.copyD2U());
    } else if (methodName == MagicNames.sqrt) {
      TypeReference[] args = meth.getParameterTypes();
      if (args[0] == TypeReference.Float) {
        Operand val = bc2ir.popFloat();
        RegisterOperand op0 = gc.temps.makeTempFloat();
        bc2ir.appendInstruction(Unary.create(FLOAT_SQRT, op0, val));
        bc2ir.push(op0.copyD2U());
      } else if (args[0] == TypeReference.Double) {
        Operand val = bc2ir.popDouble();
        RegisterOperand op0 = gc.temps.makeTempDouble();
        bc2ir.appendInstruction(Unary.create(DOUBLE_SQRT, op0, val));
        bc2ir.pushDual(op0.copyD2U());
      } else {
        if (VM.VerifyAssertions)
          VM._assert(false,"SQRT only handles Double or Float operands");
      }
    } else if (methodName == MagicNames.getObjectType) {
      Operand val = bc2ir.popRef();
      if(val.isObjectConstant()) {
        bc2ir.push(new ObjectConstantOperand(val.getType().peekType(), Offset.zero()));
      } else {
        Operand guard = BC2IR.getGuard(val);
        if (guard == null) {
          // it's magic, so assume that it's OK....
          guard = new TrueGuardOperand();
        }
        RegisterOperand tibPtr = gc.temps.makeTemp(TypeReference.TIB);
        bc2ir.appendInstruction(GuardedUnary.create(GET_OBJ_TIB, tibPtr, val, guard));
        RegisterOperand op0;
        TypeReference argType = val.getType();
        if (argType.isArrayType()) {
          op0 = gc.temps.makeTemp(TypeReference.RVMArray);
        } else {
          if (argType == TypeReference.JavaLangObject ||
              argType == TypeReference.JavaLangCloneable ||
              argType == TypeReference.JavaIoSerializable) {
            // could be an array or a class, so make op0 be a RVMType
            op0 = gc.temps.makeTemp(TypeReference.Type);
          } else {
            op0 = gc.temps.makeTemp(TypeReference.Class);
          }
        }
        bc2ir.markGuardlessNonNull(op0);
        bc2ir.appendInstruction(Unary.create(GET_TYPE_FROM_TIB, op0, tibPtr.copyD2U()));
        bc2ir.push(op0.copyD2U());
      }
    } else if (methodName == MagicNames.getArrayLength) {
      Operand val = bc2ir.popRef();
      RegisterOperand op0 = gc.temps.makeTempInt();
      bc2ir.appendInstruction(GuardedUnary.create(ARRAYLENGTH, op0, val, new TrueGuardOperand()));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.invokeClassInitializer) {
      Instruction s = Call.create0(CALL, null, bc2ir.popRef(), null);
      bc2ir.appendInstruction(s);
    } else if ((methodName == MagicNames.invokeMethodReturningObject) ||
               (methodName == MagicNames.invokeMethodReturningVoid) ||
               (methodName == MagicNames.invokeMethodReturningLong) ||
               (methodName == MagicNames.invokeMethodReturningDouble) ||
               (methodName == MagicNames.invokeMethodReturningFloat) ||
               (methodName == MagicNames.invokeMethodReturningInt)) {
      Operand spills = bc2ir.popRef();
      Operand fprmeta = bc2ir.popRef();
      Operand fprs = bc2ir.popRef();
      Operand gprs = bc2ir.popRef();
      Operand code = bc2ir.popRef();
      RegisterOperand res = null;
      if (methodName == MagicNames.invokeMethodReturningObject) {
        res = gc.temps.makeTemp(TypeReference.JavaLangObject);
        bc2ir.push(res.copyD2U());
      } else if (methodName == MagicNames.invokeMethodReturningLong) {
        res = gc.temps.makeTemp(TypeReference.Long);
        bc2ir.push(res.copyD2U(), TypeReference.Long);
      } else if (methodName == MagicNames.invokeMethodReturningDouble) {
        res = gc.temps.makeTempDouble();
        bc2ir.push(res.copyD2U(), TypeReference.Double);
      } else if (methodName == MagicNames.invokeMethodReturningFloat) {
        res = gc.temps.makeTempFloat();
        bc2ir.push(res.copyD2U(), TypeReference.Float);
      } else if (methodName == MagicNames.invokeMethodReturningInt) {
        res = gc.temps.makeTempInt();
        bc2ir.push(res.copyD2U());
      }
      RVMField target = ArchEntrypoints.reflectiveMethodInvokerInstructionsField;
      MethodOperand met = MethodOperand.STATIC(target);
      Instruction s =
          Call.create5(CALL, res, new AddressConstantOperand(target.getOffset()), met, code, gprs, fprs, fprmeta, spills);
      bc2ir.appendInstruction(s);
    } else if (methodName == MagicNames.saveThreadState) {
      Operand p1 = bc2ir.popRef();
      RVMField target = ArchEntrypoints.saveThreadStateInstructionsField;
      MethodOperand mo = MethodOperand.STATIC(target);
      bc2ir.appendInstruction(Call.create1(CALL, null, new AddressConstantOperand(target.getOffset()), mo, p1));
    } else if (methodName == MagicNames.threadSwitch) {
      Operand p2 = bc2ir.popRef();
      Operand p1 = bc2ir.popRef();
      RVMField target = ArchEntrypoints.threadSwitchInstructionsField;
      MethodOperand mo = MethodOperand.STATIC(target);
      bc2ir.appendInstruction(Call.create2(CALL, null, new AddressConstantOperand(target.getOffset()), mo, p1, p2));
    } else if (methodName == MagicNames.restoreHardwareExceptionState) {
      RVMField target = ArchEntrypoints.restoreHardwareExceptionStateInstructionsField;
      MethodOperand mo = MethodOperand.STATIC(target);
      bc2ir.appendInstruction(Call.create1(CALL,
                                           null,
                                           new AddressConstantOperand(target.getOffset()),
                                           mo,
                                           bc2ir.popRef()));
    } else if (methodName == MagicNames.prepareInt) {
      Operand offset = bc2ir.popAddress();
      Operand base = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTempInt();
      bc2ir.appendInstruction(Prepare.create(PREPARE_INT, val, base, offset, null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.prepareLong) {
      Operand offset = bc2ir.popAddress();
      Operand base = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTempLong();
      bc2ir.appendInstruction(Prepare.create(PREPARE_LONG, val, base, offset, null));
      bc2ir.pushDual(val.copyD2U());
    } else if (methodName == MagicNames.prepareObject) {
      Operand offset = bc2ir.popAddress();
      Operand base = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.JavaLangObject);
      bc2ir.appendInstruction(Prepare.create(PREPARE_ADDR, val, base, offset, null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.prepareAddress) {
      Operand offset = bc2ir.popAddress();
      Operand base = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.Address);
      bc2ir.appendInstruction(Prepare.create(PREPARE_ADDR, val, base, offset, null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.prepareWord) {
      Operand offset = bc2ir.popAddress();
      Operand base = bc2ir.popRef();
      RegisterOperand val = gc.temps.makeTemp(TypeReference.Word);
      bc2ir.appendInstruction(Prepare.create(PREPARE_ADDR, val, base, offset, null));
      bc2ir.push(val.copyD2U());
    } else if (methodName == MagicNames.attemptInt) {
      Operand newVal = bc2ir.popInt();
      Operand oldVal = bc2ir.popInt();
      Operand offset = bc2ir.popAddress();
      Operand base = bc2ir.popRef();
      RegisterOperand test = gc.temps.makeTempBoolean();
      bc2ir.appendInstruction(Attempt.create(ATTEMPT_INT, test, base, offset, oldVal, newVal, null));
      bc2ir.push(test.copyD2U());
    } else if (methodName == MagicNames.attemptLong) {
      Operand newVal = bc2ir.popLong();
      Operand oldVal = bc2ir.popLong();
      Operand offset = bc2ir.popAddress();
      Operand base = bc2ir.popRef();
      RegisterOperand test = gc.temps.makeTempBoolean();
      bc2ir.appendInstruction(Attempt.create(ATTEMPT_LONG, test, base, offset, oldVal, newVal, null));
      bc2ir.push(test.copyD2U());
    } else if (methodName == MagicNames.attemptObject) {
      Operand newVal = bc2ir.popRef();
      Operand oldVal = bc2ir.popRef();
      Operand offset = bc2ir.popAddress();
      Operand base = bc2ir.popRef();
      RegisterOperand test = gc.temps.makeTempBoolean();
      bc2ir.appendInstruction(Attempt.create(ATTEMPT_ADDR, test, base, offset, oldVal, newVal, null));
      bc2ir.push(test.copyD2U());
    } else if (methodName == MagicNames.attemptAddress) {
      Operand newVal = bc2ir.popAddress();
      Operand oldVal = bc2ir.popAddress();
      Operand offset = bc2ir.popAddress();
      Operand base = bc2ir.popRef();
      RegisterOperand test = gc.temps.makeTempBoolean();
      bc2ir.appendInstruction(Attempt.create(ATTEMPT_ADDR, test, base, offset, oldVal, newVal, null));
      bc2ir.push(test.copyD2U());
    } else if (methodName == MagicNames.attemptWord) {
      Operand newVal = bc2ir.pop();
      Operand oldVal = bc2ir.pop();
      Operand offset = bc2ir.popAddress();
      Operand base = bc2ir.popRef();
      RegisterOperand test = gc.temps.makeTempBoolean();
      bc2ir.appendInstruction(Attempt.create(ATTEMPT_ADDR, test, base, offset, oldVal, newVal, null));
      bc2ir.push(test.copyD2U());
    } else if (generatePolymorphicMagic(bc2ir, gc, meth, methodName)) {
      return true;
    } else if (methodName == MagicNames.getTimeBase) {
      RegisterOperand op0 = gc.temps.makeTempLong();
      bc2ir.appendInstruction(Nullary.create(GET_TIME_BASE, op0));
      bc2ir.pushDual(op0.copyD2U());
    } else {
      // Wasn't machine-independent, so try the machine-dependent magics next.
      return GenerateMachineSpecificMagic.generateMagic(bc2ir, gc, meth);
    }
    return true;
  } // generateMagic

  // Generate magic where the untype operational semantics is identified by name.
  // The operands' types are determined from the method signature.
  //
  static boolean generatePolymorphicMagic(BC2IR bc2ir, GenerationContext gc, MethodReference meth,
                                          Atom methodName) {
    TypeReference resultType = meth.getReturnType();
    if (methodName == MagicNames.wordFromInt || methodName == MagicNames.wordFromIntSignExtend) {
      RegisterOperand reg = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Unary.create(INT_2ADDRSigExt, reg, bc2ir.popInt()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.wordFromIntZeroExtend) {
      RegisterOperand reg = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Unary.create(INT_2ADDRZerExt, reg, bc2ir.popInt()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.wordFromLong) {
      if (VM.BuildFor64Addr) {
        RegisterOperand reg = gc.temps.makeTemp(resultType);
        bc2ir.appendInstruction(Unary.create(LONG_2ADDR, reg, bc2ir.popLong()));
        bc2ir.push(reg.copyD2U());
      } else {
        VM._assert(false); //should not reach
      }
    } else if (methodName == MagicNames.wordToInt) {
      RegisterOperand reg = gc.temps.makeTempInt();
      bc2ir.appendInstruction(Unary.create(ADDR_2INT, reg, bc2ir.popAddress()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.wordToLong) {
      RegisterOperand lreg = gc.temps.makeTempLong();
      bc2ir.appendInstruction(Unary.create(ADDR_2LONG, lreg, bc2ir.popAddress()));
      bc2ir.pushDual(lreg.copyD2U());
    } else if (methodName == MagicNames.wordToWord) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.Word);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popAddress()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.wordToAddress) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.Address);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.wordToObject) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.JavaLangObject);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.wordToObjectReference || methodName == MagicNames.wordFromObject) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.ObjectReference);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popRef()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.wordToOffset) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.Offset);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popAddress()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.wordToExtent) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.Extent);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.popAddress()));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.codeArrayAsObject) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.JavaLangObject);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.pop(TypeReference.CodeArray)));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.tibAsObject) {
      RegisterOperand reg = gc.temps.makeTemp(TypeReference.JavaLangObject);
      bc2ir.appendInstruction(Move.create(REF_MOVE, reg, bc2ir.pop(TypeReference.TIB)));
      bc2ir.push(reg.copyD2U());
    } else if (methodName == MagicNames.wordPlus) {
      Operand o2 = bc2ir.pop();
      Operand o1 = bc2ir.pop();
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      if (VM.BuildFor64Addr && o2.isInt()) {
        RegisterOperand op1 = gc.temps.makeTemp(resultType);
        bc2ir.appendInstruction(Unary.create(INT_2ADDRSigExt, op1, o2));
        bc2ir.appendInstruction(Binary.create(REF_ADD, op0, o1, op1.copyD2U()));
      } else {
        bc2ir.appendInstruction(Binary.create(REF_ADD, op0, o1, o2));
      }
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.wordMinus) {
      Operand o2 = bc2ir.pop();
      Operand o1 = bc2ir.pop();
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      if (VM.BuildFor64Addr && o2.isInt()) {
        RegisterOperand op1 = gc.temps.makeTemp(resultType);
        bc2ir.appendInstruction(Unary.create(INT_2ADDRSigExt, op1, o2));
        bc2ir.appendInstruction(Binary.create(REF_SUB, op0, o1, op1));
      } else {
        bc2ir.appendInstruction(Binary.create(REF_SUB, op0, o1, o2));
      }
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.wordDiff) {
      Operand o2 = bc2ir.pop();
      Operand o1 = bc2ir.pop();
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Binary.create(REF_SUB, op0, o1, o2));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.wordAnd) {
      Operand o2 = bc2ir.pop();
      Operand o1 = bc2ir.pop();
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Binary.create(REF_AND, op0, o1, o2));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.wordOr) {
      Operand o2 = bc2ir.pop();
      Operand o1 = bc2ir.pop();
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Binary.create(REF_OR, op0, o1, o2));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.wordXor) {
      Operand o2 = bc2ir.pop();
      Operand o1 = bc2ir.pop();
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Binary.create(REF_XOR, op0, o1, o2));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.wordNot) {
      Operand o1 = bc2ir.pop();
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Unary.create(REF_NOT, op0, o1));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.wordZero || methodName == MagicNames.wordNull) {
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Move.create(REF_MOVE, op0, new AddressConstantOperand(Address.zero())));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.wordOne) {
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Move.create(REF_MOVE, op0, new AddressConstantOperand(Address.fromIntZeroExtend(1))));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.wordMax) {
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Move.create(REF_MOVE, op0, new AddressConstantOperand(Address.max())));
      bc2ir.push(op0.copyD2U());
    } else if (methodName == MagicNames.wordIsNull) {
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Move.create(REF_MOVE, op0, new AddressConstantOperand(Address.zero())));
      ConditionOperand cond = ConditionOperand.EQUAL();
      cmpHelper(bc2ir, gc, cond, op0.copyRO());
    } else if (methodName == MagicNames.wordIsZero) {
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Move.create(REF_MOVE, op0, new AddressConstantOperand(Address.zero())));
      ConditionOperand cond = ConditionOperand.EQUAL();
      cmpHelper(bc2ir, gc, cond, op0.copyRO());
    } else if (methodName == MagicNames.wordIsMax) {
      RegisterOperand op0 = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Move.create(REF_MOVE, op0, new AddressConstantOperand(Address.max())));
      ConditionOperand cond = ConditionOperand.EQUAL();
      cmpHelper(bc2ir, gc, cond, op0.copyRO());
    } else if (methodName == MagicNames.wordEQ) {
      ConditionOperand cond = ConditionOperand.EQUAL();
      cmpHelper(bc2ir, gc, cond, null);
    } else if (methodName == MagicNames.wordNE) {
      ConditionOperand cond = ConditionOperand.NOT_EQUAL();
      cmpHelper(bc2ir, gc, cond, null);
    } else if (methodName == MagicNames.wordLT) {
      ConditionOperand cond = ConditionOperand.LOWER();
      cmpHelper(bc2ir, gc, cond, null);
    } else if (methodName == MagicNames.wordLE) {
      ConditionOperand cond = ConditionOperand.LOWER_EQUAL();
      cmpHelper(bc2ir, gc, cond, null);
    } else if (methodName == MagicNames.wordGT) {
      ConditionOperand cond = ConditionOperand.HIGHER();
      cmpHelper(bc2ir, gc, cond, null);
    } else if (methodName == MagicNames.wordGE) {
      ConditionOperand cond = ConditionOperand.HIGHER_EQUAL();
      cmpHelper(bc2ir, gc, cond, null);
    } else if (methodName == MagicNames.wordsLT) {
      ConditionOperand cond = ConditionOperand.LESS();
      cmpHelper(bc2ir, gc, cond, null);
    } else if (methodName == MagicNames.wordsLE) {
      ConditionOperand cond = ConditionOperand.LESS_EQUAL();
      cmpHelper(bc2ir, gc, cond, null);
    } else if (methodName == MagicNames.wordsGT) {
      ConditionOperand cond = ConditionOperand.GREATER();
      cmpHelper(bc2ir, gc, cond, null);
    } else if (methodName == MagicNames.wordsGE) {
      ConditionOperand cond = ConditionOperand.GREATER_EQUAL();
      cmpHelper(bc2ir, gc, cond, null);
    } else if (methodName == MagicNames.wordLsh) {
      Operand op2 = bc2ir.popInt();
      Operand op1 = bc2ir.popAddress();
      RegisterOperand res = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Binary.create(REF_SHL, res, op1, op2));
      bc2ir.push(res.copyD2U());
    } else if (methodName == MagicNames.wordRshl) {
      Operand op2 = bc2ir.popInt();
      Operand op1 = bc2ir.popAddress();
      RegisterOperand res = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Binary.create(REF_USHR, res, op1, op2));
      bc2ir.push(res.copyD2U());
    } else if (methodName == MagicNames.wordRsha) {
      Operand op2 = bc2ir.popInt();
      Operand op1 = bc2ir.popAddress();
      RegisterOperand res = gc.temps.makeTemp(resultType);
      bc2ir.appendInstruction(Binary.create(REF_SHR, res, op1, op2));
      bc2ir.push(res.copyD2U());
    } else {
      return false;
    }
    return true;
  }

  private static void cmpHelper(BC2IR bc2ir, GenerationContext gc, ConditionOperand cond,
                                Operand given_o2) {
    Operand o2 = given_o2 == null ? bc2ir.pop() : given_o2;
    Operand o1 = bc2ir.pop();
    RegisterOperand res = gc.temps.makeTempInt();
    bc2ir.appendInstruction(BooleanCmp.create(BOOLEAN_CMP_ADDR,
                                              res.copyRO(),
                                              o1,
                                              o2,
                                              cond,
                                              new BranchProfileOperand()));
    bc2ir.push(res.copyD2U());
  }

  private static LocationOperand mapToMetadata(Operand metadata) {
    if (metadata instanceof IntConstantOperand) {
      int index = ((IntConstantOperand) metadata).value;
      if (index == 0) return null;
      MemberReference mr = MemberReference.getMemberRef(index);
      return new LocationOperand(mr.asFieldReference());
    }
    return null;
  }

  private static final int LOAD_OP = 1;
  private static final int PREPARE_OP = 2;
  private static final int STORE_OP = 3;
  private static final int ATTEMPT_OP = 4;

  private static Operator getOperator(TypeReference type, int operatorClass)
      throws MagicNotImplementedException {
    if (operatorClass == LOAD_OP) {
      if (type == TypeReference.Address) return REF_LOAD;
      if (type == TypeReference.ObjectReference) return REF_LOAD;
      if (type == TypeReference.Word) return REF_LOAD;
      if (type == TypeReference.Offset) return REF_LOAD;
      if (type == TypeReference.Extent) return REF_LOAD;
      if (type == TypeReference.Int) return INT_LOAD;
      if (type == TypeReference.Byte) return BYTE_LOAD;
      if (type == TypeReference.Short) return SHORT_LOAD;
      if (type == TypeReference.Char) return USHORT_LOAD;
      if (type == TypeReference.Float) return FLOAT_LOAD;
      if (type == TypeReference.Double) return DOUBLE_LOAD;
      if (type == TypeReference.Long) return LONG_LOAD;
    } else if (operatorClass == PREPARE_OP) {
      if (type == TypeReference.Address) return PREPARE_ADDR;
      if (type == TypeReference.ObjectReference) return PREPARE_ADDR;
      if (type == TypeReference.Word) return PREPARE_ADDR;
      if (type == TypeReference.Int) return PREPARE_INT;
      if (type == TypeReference.Long) return PREPARE_LONG;
    } else if (operatorClass == ATTEMPT_OP) {
      if (type == TypeReference.Address) return ATTEMPT_ADDR;
      if (type == TypeReference.ObjectReference) return ATTEMPT_ADDR;
      if (type == TypeReference.Word) return ATTEMPT_ADDR;
      if (type == TypeReference.Int) return ATTEMPT_INT;
      if (type == TypeReference.Long) return ATTEMPT_LONG;
    } else if (operatorClass == STORE_OP) {
      if (type == TypeReference.Address) return REF_STORE;
      if (type == TypeReference.ObjectReference) return REF_STORE;
      if (type == TypeReference.Word) return REF_STORE;
      if (type == TypeReference.Offset) return REF_STORE;
      if (type == TypeReference.Extent) return REF_STORE;
      if (type == TypeReference.Int) return INT_STORE;
      if (type == TypeReference.Byte) return BYTE_STORE;
      if (type == TypeReference.Short) return SHORT_STORE;
      if (type == TypeReference.Char) return SHORT_STORE;
      if (type == TypeReference.Float) return FLOAT_STORE;
      if (type == TypeReference.Double) return DOUBLE_STORE;
      if (type == TypeReference.Long) return LONG_STORE;
    }
    String msg = " Unexpected call to getOperator";
    throw MagicNotImplementedException.UNEXPECTED(msg);
  }

  private static boolean isLoad(Atom methodName) {
    return isPrefix(MagicNames.loadPrefix, methodName.toByteArray());
  }

  private static boolean isPrepare(Atom methodName) {
    return isPrefix(MagicNames.preparePrefix, methodName.toByteArray());
  }

  /**
   * Is string <code>a</code> a prefix of string
   * <code>b</code>. String <code>b</code> is encoded as an ASCII byte
   * array.
   *
   * @param prefix  Prefix atom
   * @param b       String which may contain prefix, encoded as an ASCII
   * byte array.
   * @return <code>true</code> if <code>a</code> is a prefix of
   * <code>b</code>
   */
  @Interruptible
  private static boolean isPrefix(Atom prefix, byte[] b) {
    byte[] a = prefix.toByteArray();
    int aLen = a.length;
    if (aLen > b.length) {
      return false;
    }
    for (int i = 0; i < aLen; i++) {
      if (a[i] != b[i]) {
        return false;
      }
    }
    return true;
  }

}
