//===----------- Opcodes.cpp - Reads and compiles opcodes -----------------===//
//
//                              N3
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "N3Debug.h"

#include <cstring>

#include <llvm/Constants.h>
#include <llvm/DerivedTypes.h>
#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#include <llvm/Type.h>
#include <llvm/Function.h>
#include <llvm/Instructions.h>
#include <llvm/PassManager.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Target/TargetData.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>

#include "mvm/JIT.h"

#include "Assembly.h"
#include "CLIJit.h"
#include "CLIString.h"
#include "MSCorlib.h"
#include "N3.h"
#include "Reader.h"
#include "VMArray.h"
#include "VMClass.h"
#include "VMObject.h"
#include "VMThread.h"

#include "OpcodeNames.def"

using namespace n3;
using namespace llvm;


static inline sint8 readS1(uint8* bytecode, uint32& i) {
  return ((sint8*)bytecode)[++i];
}

static inline uint8 readU1(uint8* bytecode, uint32& i) {
  return bytecode[++i];
}

static inline sint16 readS2(uint8* bytecode, uint32& i) {
  sint16 val = readS1(bytecode, i);
  return val | (readU1(bytecode, i) << 8);
}

static inline uint16 readU2(uint8* bytecode, uint32& i) {
  uint16 val = readU1(bytecode, i);
  return val | (readU1(bytecode, i) << 8);
}

static inline sint32 readS4(uint8* bytecode, uint32& i) {
  sint32 val = readU2(bytecode, i);
  return val | (readU2(bytecode, i) << 16);
}


static inline uint32 readU4(uint8* bytecode, uint32& i) {
  return readS4(bytecode, i);
}

static inline sint64 readS8(uint8* bytecode, uint32& i) {
  uint64 val = readU4(bytecode, i);
  uint64 _val2 = readU4(bytecode, i);
  uint64 val2 = _val2 << 32;
  return val | val2;
}

typedef union ufloat_t {
  uint32 i;
  float f;
}ufloat_t;

typedef union udouble_t {
  sint64 l;
  double d;
}udouble_t;


static inline float readFloat(uint8* bytecode, uint32& i) {
  ufloat_t tmp;
  tmp.i = readU4(bytecode, i);
  return tmp.f;
}

static inline double readDouble(uint8* bytecode, uint32& i) {
  udouble_t tmp;
  tmp.l = readS8(bytecode, i);
  return tmp.d;
}


extern "C" void n3PrintExecution(char* opcode, VMMethod* meth) {
  fprintf(stderr, "executing %s %s\n", mvm::PrintBuffer(meth).cString(), opcode);
}


static void verifyType(Value*& val1, Value*& val2, BasicBlock* currentBlock) {
  const Type* t1 = val1->getType();
  const Type* t2 = val2->getType();
  if (t1 != t2) {
    if (t1->isIntegerTy() && t2->isIntegerTy()) {
      if (t1->getPrimitiveSizeInBits() < t2->getPrimitiveSizeInBits()) {
        val1 = new SExtInst(val1, t2, "", currentBlock);
      } else {
        val2 = new SExtInst(val2, t1, "", currentBlock);
      }
    } else if (t1->isFloatTy()) {
      if (t1->getPrimitiveSizeInBits() < t2->getPrimitiveSizeInBits()) {
        val1 = new FPExtInst(val1, t2, "", currentBlock);
      } else {
        val2 = new FPExtInst(val2, t1, "", currentBlock);
      }
    } else if (isa<PointerType>(t1) && isa<PointerType>(t2)) {
      val1 = new BitCastInst(val1, VMObject::llvmType, "", currentBlock);
      val2 = new BitCastInst(val2, VMObject::llvmType, "", currentBlock);
    } else if (t1->isIntegerTy() && t2 == PointerType::getUnqual(Type::getInt8Ty(getGlobalContext()))) {
      // CLI says that this is fine for some operation
      val2 = new PtrToIntInst(val2, t1, "", currentBlock);
    } else if (t2->isIntegerTy() && t1 == PointerType::getUnqual(Type::getInt8Ty(getGlobalContext()))) {
      // CLI says that this is fine for some operation
      val1 = new PtrToIntInst(val1, t2, "", currentBlock);
    }
  }
}

void convertValue(Value*& val, const Type* t1, BasicBlock* currentBlock) {
  const Type* t2 = val->getType();
  if (t1 != t2) {
    if (t1->isIntegerTy() && t2->isIntegerTy()) {
      if (t2->getPrimitiveSizeInBits() < t1->getPrimitiveSizeInBits()) {
        val = new SExtInst(val, t1, "", currentBlock);
      } else {
        val = new TruncInst(val, t1, "", currentBlock);
      }
    } else if (t1->isFloatTy() && t2->isFloatTy()) {
      if (t2->getPrimitiveSizeInBits() < t1->getPrimitiveSizeInBits()) {
        val = new FPExtInst(val, t1, "", currentBlock);
      } else {
        val = new FPTruncInst(val, t1, "", currentBlock);
      }
    } else if (isa<PointerType>(t1) && isa<PointerType>(t2)) {
      val = new BitCastInst(val, t1, "", currentBlock);
    }
  }
}

static void store(Value* val, Value* local, bool vol, 
                  BasicBlock* currentBlock, mvm::BaseIntrinsics* module) {
  const Type* contained = local->getType()->getContainedType(0);
  if (contained->isSingleValueType()) {
    if (val->getType() != contained) {
      convertValue(val, contained, currentBlock);
    }
    new StoreInst(val, local, vol, currentBlock);
  } else if (isa<PointerType>(val->getType())) {
    uint64 size = mvm::MvmModule::getTypeSize(contained);
        
    std::vector<Value*> params;
    params.push_back(new BitCastInst(local, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock));
    params.push_back(new BitCastInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock));
    params.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), size));
    params.push_back(module->constantZero);
    CallInst::Create(module->llvm_memcpy_i32, params.begin(), params.end(), "", currentBlock);
  } else {
    new StoreInst(val, local, vol, currentBlock);
  }
}

static Value* load(Value* val, const char* name, BasicBlock* currentBlock, mvm::BaseIntrinsics* module) {
  const Type* contained = val->getType()->getContainedType(0);
  if (contained->isSingleValueType()) {
    return new LoadInst(val, name, currentBlock);
  } else {
    uint64 size = mvm::MvmModule::getTypeSize(contained);
    Value* ret = new AllocaInst(contained, "", currentBlock); 
    std::vector<Value*> params;
    params.push_back(new BitCastInst(ret, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock));
    params.push_back(new BitCastInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock));
    params.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), size));
    params.push_back(module->constantZero);
    CallInst::Create(module->llvm_memcpy_i32, params.begin(), params.end(), "", currentBlock);
    return ret;
  }
}

void CLIJit::compileOpcodes(uint8* bytecodes, uint32 codeLength, VMGenericClass* genClass, VMGenericMethod* genMethod) {
  uint32 leaveIndex = 0;
  bool isVolatile = false;
  for(uint32 i = 0; i < codeLength; ++i) {
    
    if (bytecodes[i] != 0xFE) {
      PRINT_DEBUG(N3_COMPILE, 1, COLOR_NORMAL, "\t[at %5x] %-5d ", i,
                  bytecodes[i]);
      PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "compiling %s::", mvm::PrintBuffer(compilingMethod).cString());
      PRINT_DEBUG(N3_COMPILE, 1, LIGHT_CYAN, OpcodeNames[bytecodes[i]]);
      PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "\n");
    }
    
    Opinfo* opinfo = &(opcodeInfos[i]);
    if (opinfo->newBlock) {
      if (currentBlock->getTerminator() == 0) {
        branch(opinfo->newBlock, currentBlock);
      }
      setCurrentBlock(opinfo->newBlock);
    }
    currentExceptionBlock = opinfo->exceptionBlock;
    if (currentBlock->getTerminator() != 0) { // To prevent a gcj bug with useless goto
      currentBlock = createBasicBlock("gcj bug");
    }

#if N3_EXECUTE > 1
    if (bytecodes[i] == 0xFE) {
      std::vector<llvm::Value*> args;
      args.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), (int64_t)OpcodeNamesFE[bytecodes[i + 1]]));
      args.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), (int64_t)compilingMethod));
      CallInst::Create(printExecutionLLVM, args.begin(), args.end(), "", currentBlock);
    } else {
      std::vector<llvm::Value*> args;
      args.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), (int64_t)OpcodeNames[bytecodes[i]]));
      args.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), (int64_t)compilingMethod));
      CallInst::Create(printExecutionLLVM, args.begin(), args.end(), "", currentBlock);
    }
#endif
  
    if (opinfo->reqSuppl) {
      push(new LoadInst(supplLocal, "", currentBlock));
    }

    switch (bytecodes[i]) {
      
      case ADD: {
        Value* val2 = pop();
        bool isPointer = (val2->getType() == module->ptrType);
        Value* val1 = pop();
        isPointer |= (val1->getType() == module->ptrType);
        verifyType(val1, val2, currentBlock);
        Value* res = BinaryOperator::CreateAdd(val1, val2, "", currentBlock);
        if (isPointer) {
          res = new IntToPtrInst(res, module->ptrType, "", currentBlock);
        }
        push(res);
        break;
      }
      
      case ADD_OVF: {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case ADD_OVF_UN: {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case AND: {
        Value* val2 = pop();
        Value* val1 = pop();
        push(BinaryOperator::CreateAnd(val1, val2, "", currentBlock));
        break;
      }
      
#define TEST(name, read, cmpf, cmpi, offset) case name : { \
        uint32 tmp = i;       \
        Value* val2 = pop();  \
        Value* val1 = pop();  \
        BasicBlock* ifTrue = opcodeInfos[tmp + offset + read(bytecodes, i)].newBlock; \
        Value* test = 0; \
        verifyType(val1, val2, currentBlock); \
        if (val1->getType()->isFloatTy()) { \
          test = new FCmpInst(*currentBlock, FCmpInst::cmpf, val1, val2, ""); \
        } else {  \
          test = new ICmpInst(*currentBlock, ICmpInst::cmpi, val1, val2, ""); \
        } \
        BasicBlock* ifFalse = createBasicBlock("false BEQ"); \
        branch(test, ifTrue, ifFalse, currentBlock); \
        currentBlock = ifFalse; \
        break; \
      }
      
      TEST(BEQ, readS4, FCMP_OEQ, ICMP_EQ, 5);
      TEST(BEQ_S, readS1, FCMP_OEQ, ICMP_EQ, 2);
      
      TEST(BGE, readS4, FCMP_OGE, ICMP_SGE, 5);
      TEST(BGE_S, readS1, FCMP_OGE, ICMP_SGE, 2);
      TEST(BGE_UN, readS4, FCMP_UGE, ICMP_UGE, 5);
      TEST(BGE_UN_S, readS1, FCMP_UGE, ICMP_UGE, 2);
      
      TEST(BGT, readS4, FCMP_OGT, ICMP_SGT, 5);
      TEST(BGT_S, readS1, FCMP_OGT, ICMP_SGT, 2);
      TEST(BGT_UN, readS4, FCMP_UGT, ICMP_UGT, 5);
      TEST(BGT_UN_S, readS1, FCMP_UGT, ICMP_UGT, 2);

      TEST(BLE, readS4, FCMP_OLE, ICMP_SLE, 5);
      TEST(BLE_S, readS1, FCMP_OLE, ICMP_SLE, 2);
      TEST(BLE_UN, readS4, FCMP_ULE, ICMP_ULE, 5);
      TEST(BLE_UN_S, readS1, FCMP_ULE, ICMP_ULE, 2);
      
      TEST(BLT, readS4, FCMP_OLT, ICMP_SLT, 5);
      TEST(BLT_S, readS1, FCMP_OLT, ICMP_SLT, 2);
      TEST(BLT_UN, readS4, FCMP_ULT, ICMP_ULT, 5);
      TEST(BLT_UN_S, readS1, FCMP_ULT, ICMP_ULT, 2);
      
      TEST(BNE_UN, readS4, FCMP_UNE, ICMP_NE, 5);
      TEST(BNE_UN_S, readS1, FCMP_UNE, ICMP_NE, 2);

#undef TEST 
      
      case BR : {
        uint32 tmp = i;
        BasicBlock* br = opcodeInfos[tmp + 5 + readS4(bytecodes, i)].newBlock;
        branch(br, currentBlock); 
        break;
      }
      
      case BR_S : {
        uint32 tmp = i;
        BasicBlock* br = opcodeInfos[tmp + 2 + readS1(bytecodes, i)].newBlock;
        branch(br, currentBlock); 
        break;
      }

      case BREAK: break;

#define TEST(name, read, cmpf, cmpi, offset) case name : { \
        uint32 tmp = i;       \
        Value* val2 = pop();  \
        Value* val1 = Constant::getNullValue(val2->getType());  \
        BasicBlock* ifTrue = opcodeInfos[tmp + offset + read(bytecodes, i)].newBlock; \
        Value* test = 0; \
        if (val1->getType()->isFloatTy()) { \
          test = new FCmpInst(*currentBlock, FCmpInst::cmpf, val1, val2, ""); \
        } else {  \
          test = new ICmpInst(*currentBlock, ICmpInst::cmpi, val1, val2, ""); \
        } \
        BasicBlock* ifFalse = createBasicBlock("false BR"); \
        branch(test, ifTrue, ifFalse, currentBlock); \
        currentBlock = ifFalse; \
        break; \
      }

      TEST(BRFALSE, readS4, FCMP_OEQ, ICMP_EQ, 5);
      TEST(BRFALSE_S, readS1, FCMP_OEQ, ICMP_EQ, 2);
      TEST(BRTRUE, readS4, FCMP_ONE, ICMP_NE, 5);
      TEST(BRTRUE_S, readS1, FCMP_ONE, ICMP_NE, 2);

#undef TEST
      
      case CALL: {
        uint32 value = readU4(bytecodes, i);
        invoke(value, genClass, genMethod);
        break;
      }

      case CALLI: {
        VMThread::get()->getVM()->unknownError("implement me");
        break;
      }

      case CKFINITE : {
        VMThread::get()->getVM()->unknownError("implement me");
        break;
      }

      case CONV_I1 : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type->isFloatTy()) {
          push(new FPToSIInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt16Ty(getGlobalContext()) || type == Type::getInt32Ty(getGlobalContext()) || 
                   type == Type::getInt64Ty(getGlobalContext())) {
          push(new TruncInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock));
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_I2 : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type->isFloatTy()) {
          push(new FPToSIInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt32Ty(getGlobalContext()) || type == Type::getInt64Ty(getGlobalContext())) {
          push(new TruncInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt8Ty(getGlobalContext())) {
          push(new SExtInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock));
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_I4 : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type->isFloatTy()) {
          push(new FPToSIInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt64Ty(getGlobalContext())) {
          push(new TruncInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt8Ty(getGlobalContext()) || type == Type::getInt16Ty(getGlobalContext())) {
          push(new SExtInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt32Ty(getGlobalContext())) {
          push(val);
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_I8 : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type->isFloatTy()) {
          push(new FPToSIInst(val, Type::getInt64Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt8Ty(getGlobalContext()) || type == Type::getInt16Ty(getGlobalContext()) || 
                   type == Type::getInt32Ty(getGlobalContext())) {
          push(new SExtInst(val, Type::getInt64Ty(getGlobalContext()), "", currentBlock));
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_R4 : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type == Type::getDoubleTy(getGlobalContext())) {
          push(new FPTruncInst(val, Type::getFloatTy(getGlobalContext()), "", currentBlock));
        } else if (type->isIntegerTy()) {
          push(new SIToFPInst(val, Type::getFloatTy(getGlobalContext()), "", currentBlock));
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_R8 : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type == Type::getFloatTy(getGlobalContext())) {
          push(new FPExtInst(val, Type::getDoubleTy(getGlobalContext()), "", currentBlock));
        } else if (type->isIntegerTy()) {
          push(new SIToFPInst(val, Type::getDoubleTy(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getDoubleTy(getGlobalContext())) {
          push(val);
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_U1 : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type->isFloatTy()) {
          push(new FPToUIInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt16Ty(getGlobalContext()) || type == Type::getInt32Ty(getGlobalContext()) || 
                   type == Type::getInt64Ty(getGlobalContext())) {
          push(new TruncInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock));
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_U2 : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type->isFloatTy()) {
          push(new FPToUIInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt32Ty(getGlobalContext()) || type == Type::getInt64Ty(getGlobalContext())) {
          push(new TruncInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt8Ty(getGlobalContext())) {
          push(new ZExtInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock));
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_U4 : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type->isFloatTy()) {
          push(new FPToUIInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt64Ty(getGlobalContext())) {
          push(new TruncInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt8Ty(getGlobalContext()) || type == Type::getInt16Ty(getGlobalContext())) {
          push(new ZExtInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock));
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_U8 : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type->isFloatTy()) {
          push(new FPToUIInst(val, Type::getInt64Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt8Ty(getGlobalContext()) || type == Type::getInt16Ty(getGlobalContext()) || 
                   type == Type::getInt32Ty(getGlobalContext())) {
          push(new ZExtInst(val, Type::getInt64Ty(getGlobalContext()), "", currentBlock));
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }

      case CONV_I : {
        Value* val = pop();
        Value* res = 0;
        
        if (val->getType()->isIntegerTy()) {
          if (val->getType() != Type::getInt64Ty(getGlobalContext())) {
            val = new ZExtInst(val, Type::getInt64Ty(getGlobalContext()), "", currentBlock);
          }
          res = new IntToPtrInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock);
        } else if (!val->getType()->isFloatTy()) {
          res = new BitCastInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock);
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        
        push(res);
        break;
      }

      case CONV_U : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_R_UN : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type == Type::getFloatTy(getGlobalContext())) {
          push(new FPExtInst(val, Type::getDoubleTy(getGlobalContext()), "", currentBlock));
        } else if (type->isIntegerTy()) {
          push(new UIToFPInst(val, Type::getDoubleTy(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getDoubleTy(getGlobalContext())) {
          push(val);
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_OVF_I1 : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_I2 : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_I4 : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_I8 : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_U1 : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_U2 : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_U4 : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_U8 : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_I : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_U : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_I1_UN : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_I2_UN : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_I4_UN : {
        Value* val = pop();
        const Type* type = val->getType();
        if (type->isFloatTy()) {
          push(new FPToUIInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt64Ty(getGlobalContext())) {
          push(new TruncInst(val, Type::getInt8Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt8Ty(getGlobalContext()) || type == Type::getInt16Ty(getGlobalContext())) {
          push(new ZExtInst(val, Type::getInt16Ty(getGlobalContext()), "", currentBlock));
        } else if (type == Type::getInt32Ty(getGlobalContext())) {
          push(val);
        } else {
          VMThread::get()->getVM()->unknownError("implement me");
        }
        break;
      }
      
      case CONV_OVF_I8_UN : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_U1_UN : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_U2_UN : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_U4_UN : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case CONV_OVF_U8_UN : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      

      case DIV: {
        Value* two = pop();
        Value* one = pop();
        if (one->getType()->isFloatTy()) {
          convertValue(one, two->getType(), currentBlock); 
          push(BinaryOperator::CreateFDiv(one, two, "", currentBlock));
        } else {
          push(BinaryOperator::CreateSDiv(one, two, "", currentBlock));
        }
        break;
      }
      
      case DIV_UN: {
        Value* two = pop();
        Value* one = pop();
        if (one->getType()->isFloatTy()) {
          push(BinaryOperator::CreateFDiv(one, two, "", currentBlock));
        } else {
          push(BinaryOperator::CreateUDiv(one, two, "", currentBlock));
        }
        break;
      }

      case DUP: {
        push(top());
        break;
      }

      case ENDFINALLY : {
        Value* val = new LoadInst(supplLocal, "", currentBlock);
        val = new PtrToIntInst(val, Type::getInt32Ty(getGlobalContext()), "", currentBlock);
        SwitchInst* inst = SwitchInst::Create(val, leaves[0], 
                                          leaves.size(), currentBlock);
     
        uint32 index = 0; 
        for (std::vector<BasicBlock*>::iterator i = leaves.begin(), 
             e = leaves.end(); i!= e; ++i, ++index) {
          inst->addCase(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), index), *i); 
        }

        //currentBlock = bb2;

        break;
      }
      
      case JMP : {
        VMThread::get()->getVM()->error("implement me"); 
        break;
      }
 
      case LDARG_S : {
        push(new LoadInst(arguments[readU1(bytecodes, i)], "", currentBlock));
        break;
      }

      case LDARG_0 : {
        push(new LoadInst(arguments[0], "", currentBlock));
        break;
      }
      
      case LDARG_1 : {
        push(new LoadInst(arguments[1], "", currentBlock));
        break;
      }
      
      case LDARG_2 : {
        push(new LoadInst(arguments[2], "", currentBlock));
        break;
      }
      
      case LDARG_3 : {
        push(new LoadInst(arguments[3], "", currentBlock));
        break;
      }

      case LDARGA_S : {
        push(arguments[readU1(bytecodes, i)]);
        break;
      }

      case LDC_I4 : {
        push(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), readS4(bytecodes, i)));
        break;
      }
      
      case LDC_I8 : {
        push(ConstantInt::get(Type::getInt64Ty(getGlobalContext()), readS8(bytecodes, i)));
        break;
      }
      
      case LDC_R4 : {
        push(ConstantFP::get(Type::getFloatTy(getGlobalContext()), readFloat(bytecodes, i)));
        break;
      }
      
      case LDC_R8 : {
        push(ConstantFP::get(Type::getDoubleTy(getGlobalContext()), readDouble(bytecodes, i)));
        break;
      }
      
      case LDC_I4_0 : {
        push(module->constantZero);
        break;
      }
      
      case LDC_I4_1 : {
        push(module->constantOne);
        break;
      }
      
      case LDC_I4_2 : {
        push(module->constantTwo);
        break;
      }
      
      case LDC_I4_3 : {
        push(module->constantThree);
        break;
      }
      
      case LDC_I4_4 : {
        push(module->constantFour);
        break;
      }
      
      case LDC_I4_5 : {
        push(module->constantFive);
        break;
      }
      
      case LDC_I4_6 : {
        push(module->constantSix);
        break;
      }
      
      case LDC_I4_7 : {
        push(module->constantSeven);
        break;
      }
      
      case LDC_I4_8 : {
        push(module->constantEight);
        break;
      }
      
      case LDC_I4_M1 : {
        push(module->constantMinusOne);
        break;
      }
      
      case LDC_I4_S : {
        push(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), readS1(bytecodes, i)));
        break;
      }
 
      case LDIND_U1 :
      case LDIND_I1 : {
        Value* _val = pop();
        Value* val = new BitCastInst(_val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock);
        push(new LoadInst(val, "", isVolatile, currentBlock));
        isVolatile = false;
        break;
      }
      
      case LDIND_U2 :
      case LDIND_I2 : {
        Value* _val = pop();
        Value* val = new BitCastInst(_val, PointerType::getUnqual(Type::getInt16Ty(getGlobalContext())), "", currentBlock);
        push(new LoadInst(val, "", isVolatile, currentBlock));
        isVolatile = false;
        break;
      }
      
      case LDIND_U4 :
      case LDIND_I4 : {
        Value* val = pop();
        if (val->getType()->isIntegerTy()) {
          val = new IntToPtrInst(val, PointerType::getUnqual(Type::getInt32Ty(getGlobalContext())), "", currentBlock);
        } else {
          val = new BitCastInst(val, PointerType::getUnqual(Type::getInt32Ty(getGlobalContext())), "", currentBlock);
        }
        push(new LoadInst(val, "", isVolatile, currentBlock));
        isVolatile = false;
        break;
      }
      
      case LDIND_I8 : {
        Value* _val = pop();
        Value* val = new BitCastInst(_val, PointerType::getUnqual(Type::getInt64Ty(getGlobalContext())), "", currentBlock);
        push(new LoadInst(val, "", isVolatile, currentBlock));
        isVolatile = false;
        break;
      }

      case LDIND_R4 : {
        Value* _val = pop();
        Value* val = new BitCastInst(_val, PointerType::getUnqual(Type::getFloatTy(getGlobalContext())), "", currentBlock);
        push(new LoadInst(val, "", isVolatile, currentBlock));
        isVolatile = false;
        break;
      }
      
      case LDIND_R8 : {
        Value* _val = pop();
        Value* val = new BitCastInst(_val, PointerType::getUnqual(Type::getDoubleTy(getGlobalContext())), "", currentBlock);
        push(new LoadInst(val, "", isVolatile, currentBlock));
        isVolatile = false;
        break;
      }
      
      case LDIND_I : {
        Value* _val = pop();
        Value* val = new BitCastInst(_val, PointerType::getUnqual(
                                        PointerType::getUnqual(Type::getInt8Ty(getGlobalContext()))), "", currentBlock);
        push(new LoadInst(val, "", isVolatile, currentBlock));
        isVolatile = false;
        break;
      }
      
      case LDIND_REF : {
        Value* _val = pop();
        Value* val = new BitCastInst(_val, PointerType::getUnqual(
                                        PointerType::getUnqual(VMObject::llvmType)), "", currentBlock);
        push(new LoadInst(val, "", isVolatile, currentBlock));
        isVolatile = false;
        break;
      }
 
      case LDLOC_S : {
        Value* val = load(locals[readU1(bytecodes, i)], "", currentBlock, module);
        push(val);
        break;
      }
      
      case LDLOC_0 : {
        Value* val = load(locals[0], "", currentBlock, module);
        push(val);
        break;
      }
      
      case LDLOC_1 : {
        Value* val = load(locals[1], "", currentBlock, module);
        push(val);
        break;
      }
      
      case LDLOC_2 : {
        Value* val = load(locals[2], "", currentBlock, module);
        push(val);
        break;
      }
      
      case LDLOC_3 : {
        Value* val = load(locals[3], "", currentBlock, module);
        push(val);
        break;
      }
      
      case LDLOCA_S : {
        push(locals[readU1(bytecodes, i)]);
        break;
      }
      
      case LDNULL : {
        push(CLIJit::constantVMObjectNull);
        break;
      }

      case LEAVE : {
        uint32 tmp = i;
        uint32 index = tmp + 5 + readS4(bytecodes, i);
        BasicBlock* bb = opcodeInfos[index].newBlock;
        assert(bb);
        stack.clear();
        if (finallyHandlers.size()) {
          ExceptionBlockDesc* res = 0;
          for (std::vector<ExceptionBlockDesc*>::iterator i = finallyHandlers.begin(), 
               e = finallyHandlers.end(); i!= e; ++i) {
            ExceptionBlockDesc* ex = (*i);
            if (tmp >= ex->tryOffset && tmp < ex->tryOffset + ex->tryLength) {
              res = ex;
              break;
            }
          }
          if (res) {
            Value* expr = ConstantExpr::getIntToPtr(
                                    ConstantInt::get(Type::getInt64Ty(getGlobalContext()),
                                                     uint64_t (leaveIndex++)),
                                    VMObject::llvmType);

            new StoreInst(expr, supplLocal, false, currentBlock);
            branch(res->handler, currentBlock);
          } else {
            branch(bb, currentBlock);
          }
        } else {
          branch(bb, currentBlock);
        }
        break;
      }
      
      case LEAVE_S : {
        uint32 tmp = i;
        BasicBlock* bb = opcodeInfos[tmp + 2 + readS1(bytecodes, i)].newBlock;
        assert(bb);
        stack.clear();
        if (finallyHandlers.size()) {
          ExceptionBlockDesc* res = 0;
          for (std::vector<ExceptionBlockDesc*>::iterator i = finallyHandlers.begin(), 
               e = finallyHandlers.end(); i!= e; ++i) {
            ExceptionBlockDesc* ex = (*i);
            if (tmp >= ex->tryOffset && tmp < ex->tryOffset + ex->tryLength) {
              res = ex;
              break;
            }
          }
          if (res) {
            Value* expr = ConstantExpr::getIntToPtr(
                                    ConstantInt::get(Type::getInt64Ty(getGlobalContext()),
                                                     uint64_t (leaveIndex++)),
                                    VMObject::llvmType);

            new StoreInst(expr, supplLocal, false, currentBlock);
            branch(res->handler, currentBlock);
          } else {
            branch(bb, currentBlock);
          }
        } else {
          branch(bb, currentBlock);
        }
        break;
      }

      case MUL : {
        Value* val2 = pop();
        Value* val1 = pop();
        convertValue(val1, val2->getType(), currentBlock); 
        push(BinaryOperator::CreateMul(val1, val2, "", currentBlock));
        break;
      }

      case MUL_OVF : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case MUL_OVF_UN : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }

      case NEG : {
        Value* val = pop();
        push(BinaryOperator::CreateSub(
                              Constant::getNullValue(val->getType()),
                              val, "", currentBlock));
        break;
      }
      
      case NOP : break;

      case NOT : {
        push(BinaryOperator::CreateNot(pop(), "", currentBlock));
        break;
      }

      case OR : {
        Value* two = pop();
        Value* one = pop();
        push(BinaryOperator::CreateOr(one, two, "", currentBlock));
        break;
      }

      case POP : {
        pop();
        break;
      }

      case REM : {
        Value* two = pop();
        Value* one = pop();
        if (one->getType()->isFloatTy()) {
          push(BinaryOperator::CreateFRem(one, two, "", currentBlock));
        } else {
          push(BinaryOperator::CreateSRem(one, two, "", currentBlock));
        }
        break;
      }
      
      case REM_UN : {
        Value* two = pop();
        Value* one = pop();
        if (one->getType()->isFloatTy()) {
          push(BinaryOperator::CreateFRem(one, two, "", currentBlock));
        } else {
          push(BinaryOperator::CreateURem(one, two, "", currentBlock));
        }
        break;
      }

      case RET : {
        if (compilingMethod->getSignature(genMethod)->getReturnType() != Type::getVoidTy(getGlobalContext())) {
          Value* val = pop();
          if (val->getType() == PointerType::getUnqual(endNode->getType())) {
            // In case it's a struct
            val = new LoadInst(val, "", currentBlock);
          } else {
            convertValue(val, endNode->getType(), currentBlock);
          }
          endNode->addIncoming(val, currentBlock);
        } else if (compilingMethod->structReturn) {
          endNode->addIncoming(pop(), currentBlock);
        }
        BranchInst::Create(endBlock, currentBlock);
        break;
      }
      
      case SHL : {
        Value* val2 = pop();
        Value* val1 = pop();
        verifyType(val1, val2, currentBlock);
        push(BinaryOperator::CreateShl(val1, val2, "", currentBlock));
        break;
      }
      
      case SHR : {
        Value* val2 = pop();
        Value* val1 = pop();
        verifyType(val1, val2, currentBlock);
        push(BinaryOperator::CreateAShr(val1, val2, "", currentBlock));
        break;
      }
      
      case SHR_UN : {
        Value* val2 = pop();
        Value* val1 = pop();
        verifyType(val1, val2, currentBlock);
        push(BinaryOperator::CreateLShr(val1, val2, "", currentBlock));
        break;
      }
 
      case STARG_S : {
        Value* val = pop();
        Value* arg = arguments[readU1(bytecodes, i)];
        convertValue(val, arg->getType()->getContainedType(0), currentBlock);
        new StoreInst(val, arg, false, currentBlock);
        break;
      }

      case STIND_I1 : {
        Value* val = pop();
        Value* _addr = pop();
        Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "",
                                      currentBlock);
        convertValue(val, Type::getInt8Ty(getGlobalContext()), currentBlock);
        new StoreInst(val, addr, isVolatile, currentBlock);
        isVolatile = false;
        break;
      }
      
      case STIND_I2 : {
        Value* val = pop();
        Value* _addr = pop();
        Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getInt16Ty(getGlobalContext())), 
                                      "", currentBlock);
        new StoreInst(val, addr, isVolatile, currentBlock);
        isVolatile = false;
        break;
      }
      
      case STIND_I4 : {
        Value* val = pop();
        Value* _addr = pop();
        Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getInt32Ty(getGlobalContext())), 
                                      "", currentBlock);
        new StoreInst(val, addr, isVolatile, currentBlock);
        isVolatile = false;
        break;
      }
      
      case STIND_I8 : {
        Value* val = pop();
        Value* _addr = pop();
        Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getInt64Ty(getGlobalContext())), 
                                      "", currentBlock);
        new StoreInst(val, addr, isVolatile, currentBlock);
        isVolatile = false;
        break;
      }
      
      case STIND_R4 : {
        Value* val = pop();
        Value* _addr = pop();
        Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getFloatTy(getGlobalContext())), 
                                      "", currentBlock);
        new StoreInst(val, addr, isVolatile, currentBlock);
        isVolatile = false;
        break;
      }
      
      case STIND_R8 : {
        Value* val = pop();
        Value* _addr = pop();
        Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getDoubleTy(getGlobalContext())), 
                                      "", currentBlock);
        new StoreInst(val, addr, isVolatile, currentBlock);
        isVolatile = false;
        break;
      }
      
      case STIND_I : {
        Value* val = pop();
        Value* _addr = pop();
        Value* addr = new BitCastInst(_addr, PointerType::getUnqual(Type::getInt32Ty(getGlobalContext())), 
                                      "", currentBlock);
        new StoreInst(val, addr, isVolatile, currentBlock);
        isVolatile = false;
        break;
      }
      
      case STIND_REF : {
        Value* val = pop();
        Value* _addr = pop();
        Value* addr = new BitCastInst(_addr, PointerType::getUnqual(val->getType()), 
                                      "", currentBlock);
        new StoreInst(val, addr, isVolatile, currentBlock);
        isVolatile = false;
        break;
      }
      
      case STLOC_S : {
        Value* val = pop();
        Value* local = locals[readU1(bytecodes, i)];
        store(val, local, false, currentBlock, module);
        break;
      }
      
      case STLOC_0 : {
        Value* val = pop();
        Value* local = locals[0];
        store(val, local, false, currentBlock, module);
        break;
      }
      
      case STLOC_1 : {
        Value* val = pop();
        Value* local = locals[1];
        store(val, local, false, currentBlock, module);
        break;
      }
      
      case STLOC_2 : {
        Value* val = pop();
        Value* local = locals[2];
        store(val, local, false, currentBlock, module);
        break;
      }
      
      case STLOC_3 : {
        Value* val = pop();
        Value* local = locals[3];
        store(val, local, false, currentBlock, module);
        break;
      }
      
      case SUB : {
        Value* val2 = pop();
        Value* val1 = pop();
        verifyType(val1, val2, currentBlock);
        push(BinaryOperator::CreateSub(val1, val2, "", currentBlock));
        break;
      }

      case SUB_OVF : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case SUB_OVF_UN : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }

      case SWITCH : {
        uint32 value = readU4(bytecodes, i);
        Value* val = pop();
        uint32 next = i + value * sizeof(sint32) + 1;
        BasicBlock* defBB = opcodeInfos[next].newBlock;
        SwitchInst* SI = SwitchInst::Create(val, defBB, value, currentBlock);
        for (uint32 t = 0; t < value; t++) {
          sint32 offset = readS4(bytecodes, i);
          sint32 index = next + offset;
          assert(index > 0);
          BasicBlock* BB = opcodeInfos[index].newBlock;
          SI->addCase(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), t), BB);
        }
        break;
      }

      case XOR : {
        Value* two = pop();
        Value* one = pop();
        convertValue(two, one->getType(), currentBlock);
        push(BinaryOperator::CreateXor(one, two, "", currentBlock));
        break;
      }

      case BOX : {
        uint32 token = readU4(bytecodes, i);
        Assembly* assembly = compilingClass->assembly;
        N3* vm = (N3*)(VMThread::get()->getVM());
        VMCommonClass* type = assembly->loadType(vm, token, true, false, false,
                                                 true, genClass, genMethod);
        assert(type);
        
        if (!type->isPrimitive) {
          // the box instruction has no effect on non-primitive types
          break;
        }

        Value* var = new LoadInst(type->llvmVar(), "", currentBlock);
        Value* obj = CallInst::Create(objConsLLVM, var, "", currentBlock);
        Value* val = pop();
        obj = new BitCastInst(obj, type->virtualType, "", currentBlock);
    

        std::vector<Value*> ptrs;
        ptrs.push_back(module->constantZero);
        ptrs.push_back(module->constantOne);
        Value* ptr = GetElementPtrInst::Create(obj, ptrs.begin(), ptrs.end(), "", 
                                           currentBlock);

        if (val->getType()->getTypeID() != Type::PointerTyID) {
          convertValue(val, type->naturalType, currentBlock); 
          Value* tmp = new AllocaInst(type->naturalType, "", currentBlock);
          new StoreInst(val, tmp, false, currentBlock);
          val = tmp;
        }
        
        
        uint64 size = mvm::MvmModule::getTypeSize(type->naturalType);
        
        std::vector<Value*> params;
        params.push_back(new BitCastInst(ptr, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock));
        params.push_back(new BitCastInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock));
        params.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), size));
        params.push_back(module->constantZero);
        CallInst::Create(module->llvm_memcpy_i32, params.begin(), params.end(), "", currentBlock);
        

        push(obj);
        break; 
      }

      case CALLVIRT : {
        uint32 value = readU4(bytecodes, i);
        invokeInterfaceOrVirtual(value, genClass, genMethod);
        break; 
      }

      case CASTCLASS : {
        Assembly* assembly = compilingClass->assembly;
        N3* vm = (N3*)(VMThread::get()->getVM());
        uint32 token = readU4(bytecodes, i);
        VMCommonClass* dcl = assembly->loadType(vm, token, true, false,
                                               false, true, genClass, genMethod);
        Value* obj = new BitCastInst(pop(), VMObject::llvmType, "", currentBlock);

        Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, obj, 
                                  CLIJit::constantVMObjectNull, "");
     
        BasicBlock* ifTrue = createBasicBlock("null checkcast");
        BasicBlock* ifFalse = createBasicBlock("non null checkcast");

        branch(cmp, ifTrue, ifFalse, currentBlock);

        Value* clVar = new LoadInst(dcl->llvmVar(), "", ifFalse);
        std::vector<Value*> args;
        args.push_back(obj);
        args.push_back(clVar);
        Value* call = CallInst::Create(instanceOfLLVM, args.begin(), args.end(),
                                   "", ifFalse);
     
        cmp = new ICmpInst(*ifFalse, ICmpInst::ICMP_EQ, call,
                           module->constantZero, "");

        BasicBlock* ex = createBasicBlock("false checkcast");
        branch(cmp, ex, ifTrue, ifFalse);
        
        std::vector<Value*> exArgs;
        if (currentExceptionBlock != endExceptionBlock) {
          InvokeInst::Create(classCastExceptionLLVM, unifiedUnreachable, currentExceptionBlock, exArgs.begin(), exArgs.end(), "", ex);
        } else {
          CallInst::Create(classCastExceptionLLVM, exArgs.begin(), exArgs.end(), "", ex);
          new UnreachableInst(getGlobalContext(), ex);
        }

        currentBlock = ifTrue;
        push(new BitCastInst(obj, dcl->virtualType, "", currentBlock));
        break;
      } 
        

      case CPOBJ : {
        VMThread::get()->getVM()->error("implement me");
        break;  
      }
       
      case ISINST : {
        Assembly* assembly = compilingClass->assembly;
        N3* vm = (N3*)(VMThread::get()->getVM());
        uint32 token = readU4(bytecodes, i);
        VMCommonClass* dcl = assembly->loadType(vm, token, true, false,
                                               false, true, genClass, genMethod);
        Value* obj = pop();

        Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, obj, 
                                  Constant::getNullValue(obj->getType()), "");
        Constant* nullVirtual = Constant::getNullValue(dcl->virtualType);

     
        BasicBlock* isInstEndBlock = createBasicBlock("end isinst");
        PHINode* node = PHINode::Create(dcl->virtualType, "", isInstEndBlock);

        BasicBlock* ifFalse = createBasicBlock("non null isinst");
        BasicBlock* ifTrue = createBasicBlock("null isinst");
        
        BranchInst::Create(ifTrue, ifFalse, cmp, currentBlock);
        node->addIncoming(nullVirtual, ifTrue);
        BranchInst::Create(isInstEndBlock, ifTrue);


        Value* clVar = new LoadInst(dcl->llvmVar(), "", ifFalse);
        std::vector<Value*> args;
        args.push_back(new BitCastInst(obj, VMObject::llvmType, "", ifFalse));
        args.push_back(clVar);
        Value* call = CallInst::Create(instanceOfLLVM, args.begin(), args.end(),
                                   "", ifFalse);
     
        cmp = new ICmpInst(*ifFalse, ICmpInst::ICMP_EQ, call,
                           module->constantZero, "");

        BasicBlock* falseInst = createBasicBlock("false isinst");
        BasicBlock* trueInst = createBasicBlock("true isinst");
        BranchInst::Create(falseInst, trueInst, cmp, ifFalse);

        node->addIncoming(new BitCastInst(obj, dcl->virtualType, "", trueInst), trueInst);
        BranchInst::Create(isInstEndBlock, trueInst);
        
        node->addIncoming(nullVirtual, falseInst);
        BranchInst::Create(isInstEndBlock, falseInst);
       
        currentBlock = isInstEndBlock;
        push(node);
        break;
      }
      
      case LDELEM : {
        VMThread::get()->getVM()->error("implement me");
        break;  
      }
      
      case LDELEM_I1 : {
        VMThread::get()->getVM()->error("implement me");
        break;  
      }

      case LDELEM_I2 : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArraySInt16::llvmType);
        push(new LoadInst(ptr, "", currentBlock));
        break;
      }
      
      case LDELEM_I4 : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArraySInt32::llvmType);
        push(new LoadInst(ptr, "", currentBlock));
        break;
      }
      
      case LDELEM_I8 : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayUInt64::llvmType);
        push(new LoadInst(ptr, "", currentBlock));
        break;
      }
      
      case LDELEM_U1 : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayUInt8::llvmType);
        push(new LoadInst(ptr, "", currentBlock));
        break;
      }
      
      case LDELEM_U2 : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayUInt16::llvmType);
        push(new LoadInst(ptr, "", currentBlock));
        break;
      }
      
      case LDELEM_U4 : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayUInt32::llvmType);
        push(new LoadInst(ptr, "", currentBlock));
        break;
      }
      
      case LDELEM_R4 : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayFloat::llvmType);
        push(new LoadInst(ptr, "", currentBlock));
        break;
      }
      
      case LDELEM_R8 : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayDouble::llvmType);
        push(new LoadInst(ptr, "", currentBlock));
        break;
      }
      
      case LDELEM_I : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArraySInt32::llvmType);
        push(new LoadInst(ptr, "", currentBlock));
        break;
      }
      
      case LDELEM_REF : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayObject::llvmType);
        push(new LoadInst(ptr, "", currentBlock));
        break;
      }

      case LDELEMA : {
        Assembly* assembly = compilingClass->assembly;
        N3* vm = (N3*)(VMThread::get()->getVM());
        uint32 token = readU4(bytecodes, i);
        VMCommonClass* cl = assembly->loadType(vm, token, true, false,
                                               false, true, genClass, genMethod);
        VMClassArray* array = assembly->constructArray(cl, 1);
        array->resolveType(false, false, genMethod);
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, array->naturalType);
        push(ptr);
        break;
      }

      case LDFLD : {
        uint32 value = readU4(bytecodes, i);
        Value* val = getVirtualField(value, genClass, genMethod);
        push(new LoadInst(val, "", isVolatile, currentBlock));
        isVolatile = false;
        break;
      }
      
      case LDFLDA : {
        uint32 value = readU4(bytecodes, i);
        push(getVirtualField(value, genClass, genMethod));
        break;
      }

      case LDLEN : {
        push(arraySize(pop()));
        break;
      }

      case LDOBJ : {
        Assembly* assembly = compilingClass->assembly;
        N3* vm = (N3*)(VMThread::get()->getVM());
        uint32 token = readU4(bytecodes, i);
        VMCommonClass* cl = assembly->loadType(vm, token, true, false,
                                               false, true, genClass, genMethod);
        if (!(cl->super == MSCorlib::pValue || cl->super == MSCorlib::pEnum)) {
          push(new LoadInst(pop(), "", isVolatile, currentBlock));
          isVolatile = false;
        } 
        break;
      }

      case LDSFLD : {
        uint32 value = readU4(bytecodes, i);
        Value* val = getStaticField(value, genClass, genMethod);
        push(new LoadInst(val, "", isVolatile, currentBlock));
        isVolatile = false;
        break;
      }
      
      case LDSFLDA : {
        uint32 value = readU4(bytecodes, i);
        push(getStaticField(value, genClass, genMethod));
        break;
      }

      case LDSTR : {
        uint32 value = readU4(bytecodes, i);
        uint32 index = value & 0xfffffff;
				// must modify this opcode
        declare_gcroot(const ArrayChar*, array) = compilingClass->assembly->readUserString(index);
        Value* val = ConstantExpr::getIntToPtr(ConstantInt::get(Type::getInt64Ty(getGlobalContext()), (int64_t)array),
                                               module->ptrType);
        Value* res = CallInst::Create(newStringLLVM, val, "", currentBlock);
        /*CLIString * str = 
          (CLIString*)(((N3*)VMThread::get()->getVM())->UTF8ToStr(utf8));
        GlobalVariable* gv = str->llvmVar();
        push(new BitCastInst(new LoadInst(gv, "", currentBlock), 
                             MSCorlib::pString->naturalType, "", currentBlock));*/
        push(new BitCastInst(res, MSCorlib::pString->naturalType, "", currentBlock));
        break;
      }

      case LDTOKEN : {
        uint32 token = readU4(bytecodes, i);
        uint32 table = token >> 24;
        Assembly* assembly = compilingClass->assembly;
        N3* vm = (N3*)(VMThread::get()->getVM());
        switch (table) {
          case CONSTANT_Field : {
            uint32 typeToken = assembly->getTypedefTokenFromField(token);
            assembly->loadType(vm, typeToken, true, true, false, true, genClass, genMethod);
            VMField* field = assembly->lookupFieldFromToken(token);
            if (!field) {
              VMThread::get()->getVM()->error("implement me");
            }
            Value* arg = new LoadInst(field->llvmVar(), "", currentBlock);
            push(arg);
            break;
          }
          
          case CONSTANT_MethodDef : {
            uint32 typeToken = assembly->getTypedefTokenFromMethod(token);
            assembly->loadType(vm, typeToken, true, true, false, true, genClass, genMethod);
            VMMethod* meth = assembly->lookupMethodFromToken(token);
            if (!meth) {
              VMThread::get()->getVM()->error("implement me");
            }
            Value* arg = new LoadInst(meth->llvmVar(), "", currentBlock);
            push(arg);
            break;
          }

          case CONSTANT_TypeDef :
          case CONSTANT_TypeRef : {
            VMCommonClass* cl = assembly->loadType(vm, token, true, false,
                                                   false, true, genClass, genMethod);
            Value* arg = new LoadInst(cl->llvmVar(), "", currentBlock);
            push(arg);
            break;
          }

          default :
            VMThread::get()->getVM()->error("implement me");
        }
        break;
      }
      
      case MKREFANY : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }

      case NEWARR : {
        uint32 value = readU4(bytecodes, i);
        Assembly* ass = compilingClass->assembly;
        VMCommonClass* baseType = ass->loadType((N3*)(VMThread::get()->getVM()),
                                                value, true, false, false, 
                                                true, genClass, genMethod);

        VMClassArray* type = ass->constructArray(baseType, 1);
        type->resolveType(false, false, genMethod);
        Value* var = new LoadInst(type->llvmVar(), "", currentBlock);
        std::vector<Value*> args;
        args.push_back(var);
        args.push_back(pop());
        Value* val = CallInst::Create(arrayConsLLVM, args.begin(), args.end(), "",
                                  currentBlock);
        push(new BitCastInst(val, type->naturalType, "", currentBlock));
        break;
      }

      case NEWOBJ : {
        uint32 value = readU4(bytecodes, i);
        invokeNew(value, genClass, genMethod);
        break;
      }
      
      case REFANYVAL : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case STELEM : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }

      case STELEM_I1 : {
        Value* val = pop();
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArraySInt8::llvmType);
        convertValue(val, Type::getInt8Ty(getGlobalContext()), currentBlock);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }
      
      case STELEM_I2 : {
        Value* val = pop();
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArraySInt16::llvmType);
        convertValue(val, Type::getInt16Ty(getGlobalContext()), currentBlock);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }
      
      case STELEM_I4 : {
        Value* val = pop();
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArraySInt32::llvmType);
        convertValue(val, Type::getInt32Ty(getGlobalContext()), currentBlock);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }
      
      case STELEM_I8 : {
        Value* val = pop();
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayUInt64::llvmType);
        convertValue(val, Type::getInt64Ty(getGlobalContext()), currentBlock);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }
      
      case STELEM_R4 : {
        Value* val = pop();
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayFloat::llvmType);
        convertValue(val, Type::getFloatTy(getGlobalContext()), currentBlock);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }
      
      case STELEM_R8 : {
        Value* val = pop();
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayDouble::llvmType);
        convertValue(val, Type::getDoubleTy(getGlobalContext()), currentBlock);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }

      case STELEM_I : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }

      case STELEM_REF : {
        Value* val = new BitCastInst(pop(), VMObject::llvmType, "", 
                                     currentBlock);
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, ArrayObject::llvmType);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }
      
      case STFLD : {
        uint32 index = readU4(bytecodes, i);
        setVirtualField(index, isVolatile, genClass, genMethod);
        isVolatile = false;
        break;
      }

      case STOBJ : {
        VMThread::get()->getVM()->error("implement me");
        isVolatile = false;
        break;
      }

      case STSFLD : {
        uint32 index = readU4(bytecodes, i);
        setStaticField(index, isVolatile, genClass, genMethod);
        isVolatile = false;
        break;
      }

      case THROW : {
        llvm::Value* arg = pop();
        arg = new BitCastInst(arg, VMObject::llvmType, "", currentBlock);
        std::vector<Value*> args;
        args.push_back(arg);
        if (currentExceptionBlock != endExceptionBlock) {
          InvokeInst::Create(throwExceptionLLVM, unifiedUnreachable, currentExceptionBlock, args.begin(), args.end(), "", currentBlock);
        } else {
          CallInst::Create(throwExceptionLLVM, args.begin(), args.end(), "", currentBlock);
          new UnreachableInst(getGlobalContext(), currentBlock);
        }
        break;
      }

      case UNBOX : {
        uint32 token = readU4(bytecodes, i);
        Assembly* assembly = compilingClass->assembly;
        N3* vm = (N3*)(VMThread::get()->getVM());
        VMCommonClass* type = assembly->loadType(vm, token, true, false, false,
                                                 true, genClass, genMethod);
        assert(type);

        Value* val = new AllocaInst(type->naturalType, "", currentBlock);
        Value* obj = pop();

        if (obj->getType() != type->virtualType) {
          obj = new BitCastInst(obj, type->virtualType, "", currentBlock);
        }
        
        std::vector<Value*> ptrs;
        ptrs.push_back(module->constantZero);
        ptrs.push_back(module->constantOne);
        Value* ptr = GetElementPtrInst::Create(obj, ptrs.begin(), ptrs.end(), "", 
                                           currentBlock);

        uint64 size = mvm::MvmModule::getTypeSize(type->naturalType);
        
        std::vector<Value*> params;
        params.push_back(new BitCastInst(val, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock));
        params.push_back(new BitCastInst(ptr, PointerType::getUnqual(Type::getInt8Ty(getGlobalContext())), "", currentBlock));
        params.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), size));
        params.push_back(module->constantZero);
        CallInst::Create(module->llvm_memcpy_i32, params.begin(), params.end(), "", currentBlock);
        

        push(val);
        break;
      }
      
      case UNBOX_ANY : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }

      case 0xFE : {
        PRINT_DEBUG(N3_COMPILE, 1, COLOR_NORMAL, "\t[at %5x] %-5d ", i,
                    bytecodes[i + 1]);
        PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "compiling %s::", mvm::PrintBuffer(compilingMethod).cString());
        PRINT_DEBUG(N3_COMPILE, 1, LIGHT_CYAN, OpcodeNamesFE[bytecodes[i + 1]]);
        PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "\n");

        switch (bytecodes[++i]) {

#define TEST(name, cmpf, cmpi) case name : { \
          Value* val2 = pop(); \
          Value* val1 = pop(); \
          Value* test = 0; \
          if (val1->getType()->isFloatTy()) { \
            test = new FCmpInst(*currentBlock, FCmpInst::cmpf, val1, val2, ""); \
          } else { \
            convertValue(val2, val1->getType(), currentBlock); \
            test = new ICmpInst(*currentBlock, ICmpInst::cmpi, val1, val2, ""); \
          } \
          push(test); \
          break; \
        }
      
        TEST(CEQ, FCMP_OEQ, ICMP_EQ);
        TEST(CGT, FCMP_OGT, ICMP_SGT);
        TEST(CGT_UN, FCMP_UGT, ICMP_UGT);
        TEST(CLT, FCMP_OLT, ICMP_SLT);
        TEST(CLT_UN, FCMP_ULT, ICMP_ULT);

#undef TEST
          
          case CPBLK : {
            Value* three = pop();
            Value* two = pop();
            Value* one = pop();
            std::vector<Value*> args;
            args.push_back(one);
            args.push_back(two);
            args.push_back(three);
            CallInst::Create(module->llvm_memcpy_i32,
                         args.begin(), args.end(), "", currentBlock);
            isVolatile = false;
            break;
          }
      
          case ENDFILTER: {
            VMThread::get()->getVM()->error("implement me");
            break;
          }
          
          case LDARG : {
            push(new LoadInst(arguments[readU2(bytecodes, i)], "", currentBlock));
            break;
          }
          
          case LDARGA : {
            push(arguments[readU2(bytecodes, i)]);
            break;
          }
          
          case LDFTN : {
            Assembly* assembly = compilingClass->assembly;
            N3* vm = (N3*)(VMThread::get()->getVM());
            uint32 token = readU4(bytecodes, i);
            uint32 table = token >> 24;
            if (table == CONSTANT_MethodDef) {
              uint32 typeToken = assembly->getTypedefTokenFromMethod(token);
              assembly->loadType(vm, typeToken, true, false,  false, true, genClass, genMethod);
              VMMethod* meth = assembly->lookupMethodFromToken(token);
              if (!meth) VMThread::get()->getVM()->error("implement me");
              Value* arg = new LoadInst(meth->llvmVar(), "", currentBlock);
              push(arg);
            } else {
              VMThread::get()->getVM()->error("implement me");
            }
            break;
          }

          case INITBLK : {
            Value* three = pop();
            Value* two = pop();
            Value* one = pop();
            std::vector<Value*> args;
            args.push_back(one);
            args.push_back(two);
            args.push_back(three);
            CallInst::Create(module->llvm_memset_i32,
                         args.begin(), args.end(), "", currentBlock);
            isVolatile = false;
            break;
          }
      
          case LDLOC : {
            push(new LoadInst(locals[readU2(bytecodes, i)], "", currentBlock));
            break;
          }
          
          case LOCALLOC : {
            push(new AllocaInst(Type::getInt8Ty(getGlobalContext()), pop(), "", currentBlock));
            break;
          }
          
          case STARG : {
            new StoreInst(pop(), arguments[readU2(bytecodes, i)], false, currentBlock);
            break;
          }
          
          case STLOC : {
            Value* val = pop();
            Value* local = locals[readU2(bytecodes, i)];
            store(val, local, false, currentBlock, module);
            break;
          }
          
          case LDLOCA : {
            push(locals[readU2(bytecodes, i)]);
            break;
          }
      
          case ARGLIST : {
            VMThread::get()->getVM()->error("implement me");
            break;
          }
      
          case INITOBJ : {
            uint32 token = readU4(bytecodes, i);
            Assembly* assembly = compilingClass->assembly;
            N3* vm = (N3*)(VMThread::get()->getVM());
            VMCommonClass* type = assembly->loadType(vm, token, true, false, false,
                                                     true, genClass, genMethod);
            if (type->super == MSCorlib::pValue) {
              uint64 size = mvm::MvmModule::getTypeSize(type->naturalType);
        
              std::vector<Value*> params;
              params.push_back(new BitCastInst(pop(), module->ptrType, "",
                                               currentBlock));
              params.push_back(module->constantInt8Zero);
              params.push_back(ConstantInt::get(Type::getInt32Ty(getGlobalContext()), size));
              params.push_back(module->constantZero);
              CallInst::Create(module->llvm_memset_i32, params.begin(),
                               params.end(), "", currentBlock);
            }

            break;  
          }
          
          case LDVIRTFTN : {
            VMThread::get()->getVM()->error("implement me");
            break;
          }
          
          case REFANYTYPE : {
            VMThread::get()->getVM()->error("implement me");
            break;
          }
          
          case RETHROW : {
            std::vector<Value*> args;
            if (opinfo->exception) {
              args.push_back(opinfo->exception);
            } else {
              args.push_back(CLIJit::constantVMObjectNull);
            }
            if (currentExceptionBlock != endExceptionBlock) {
              InvokeInst::Create(throwExceptionLLVM, unifiedUnreachable, currentExceptionBlock, args.begin(), args.end(), "", currentBlock);
            } else {
              CallInst::Create(throwExceptionLLVM, args.begin(), args.end(), "", currentBlock);
              new UnreachableInst(getGlobalContext(), currentBlock);
            }
            break;
          }
        
          case SIZEOF : {
            VMThread::get()->getVM()->error("implement me");
            break;
          }

          case VOLATILE_ : {
            isVolatile = true;
            break;
          }
          default :
            VMThread::get()->getVM()->unknownError("unknown bytecode");
        } 
        break;
      }

      default :
        VMThread::get()->getVM()->unknownError("unknown bytecode");
    } 
  }
}

void CLIJit::exploreOpcodes(uint8* bytecodes, uint32 codeLength) {
  for(uint32 i = 0; i < codeLength; ++i) {
    
    if (bytecodes[i] != 0xFE) {
      PRINT_DEBUG(N3_COMPILE, 1, COLOR_NORMAL, "\t[at %5x] %-5d ", i,
                  bytecodes[i]);
      PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "exploring %s::", mvm::PrintBuffer(compilingMethod).cString());
      PRINT_DEBUG(N3_COMPILE, 1, LIGHT_CYAN, OpcodeNames[bytecodes[i]]);
      PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "\n");
    }
    
    switch (bytecodes[i]) {
      
      case ADD:
      case ADD_OVF:
      case ADD_OVF_UN:
      case AND: break;
      
#define TEST(name, read, offset) case name : { \
        uint32 tmp = i; \
        uint16 index = tmp + offset + read(bytecodes, i); \
        if (!(opcodeInfos[index].newBlock)) \
          opcodeInfos[index].newBlock = createBasicBlock("Branches"); \
        break; \
      }
      
      TEST(BEQ, readS4, 5);
      TEST(BEQ_S, readS1, 2);
      
      TEST(BGE, readS4, 5);
      TEST(BGE_S, readS1, 2);
      TEST(BGE_UN, readS4, 5);
      TEST(BGE_UN_S, readS1, 2);
      
      TEST(BGT, readS4, 5);
      TEST(BGT_S, readS1, 2);
      TEST(BGT_UN, readS4, 5);
      TEST(BGT_UN_S, readS1, 2);

      TEST(BLE, readS4, 5);
      TEST(BLE_S, readS1, 2);
      TEST(BLE_UN, readS4, 5);
      TEST(BLE_UN_S, readS1, 2);
      
      TEST(BLT, readS4, 5);
      TEST(BLT_S, readS1, 2);
      TEST(BLT_UN, readS4, 5);
      TEST(BLT_UN_S, readS1, 2);
      
      TEST(BNE_UN, readS4, 5);
      TEST(BNE_UN_S, readS1, 2);

      case BR : {
        uint32 tmp = i;
        uint16 index = tmp + 5 + readS4(bytecodes, i);
        if (!(opcodeInfos[index].newBlock))
          opcodeInfos[index].newBlock = createBasicBlock("BR");
        break;
      }
      
      case BR_S : {
        uint32 tmp = i;
        uint16 index = tmp + 2 + readS1(bytecodes, i);
        if (!(opcodeInfos[index].newBlock))
          opcodeInfos[index].newBlock = createBasicBlock("BR");
        break;
      }

      case BREAK: break;

      TEST(BRFALSE, readS4, 5);
      TEST(BRFALSE_S, readS1, 2);
      TEST(BRTRUE, readS4, 5);
      TEST(BRTRUE_S, readS1, 2);

#undef TEST
      
      case CALL: {
        i+= 4;
        break;
      }

      case CALLI: {
        VMThread::get()->getVM()->unknownError("implement me");
        break;
      }

      case CKFINITE : {
        VMThread::get()->getVM()->unknownError("implement me");
        break;
      }

      case CONV_I1 :
      case CONV_I2 :
      case CONV_I4 :
      case CONV_I8 :
      case CONV_R4 :
      case CONV_R8 :
      case CONV_U1 :
      case CONV_U2 :
      case CONV_U4 :
      case CONV_U8 :
      case CONV_I :
      case CONV_U :
      case CONV_R_UN :
      case CONV_OVF_I1 :
      case CONV_OVF_I2 :
      case CONV_OVF_I4 :
      case CONV_OVF_I8 :
      case CONV_OVF_U1 :
      case CONV_OVF_U2 :
      case CONV_OVF_U4 :
      case CONV_OVF_U8 :
      case CONV_OVF_I :
      case CONV_OVF_U :
      case CONV_OVF_I1_UN :
      case CONV_OVF_I2_UN :
      case CONV_OVF_I4_UN :
      case CONV_OVF_I8_UN :
      case CONV_OVF_U1_UN :
      case CONV_OVF_U2_UN :
      case CONV_OVF_U4_UN :
      case CONV_OVF_U8_UN :
      case DIV:
      case DIV_UN:
      case DUP:
      case ENDFINALLY : break;
      
      case JMP : {
        VMThread::get()->getVM()->error("implement me"); 
        break;
      }
 
      case LDARG_S : {
        i+= 1;
        break;
      }

      case LDARG_0 :
      case LDARG_1 :
      case LDARG_2 :
      case LDARG_3 : break;

      case LDARGA_S : {
        i += 1;
        break;
      }

      case LDC_I4 : {
        i += 4;
        break;
      }
      
      case LDC_I8 : {
        i += 8;
        break;
      }
      
      case LDC_R4 : {
        i += 4;
        break;
      }
      
      case LDC_R8 : {
        i += 8;
        break;
      }
      
      case LDC_I4_0 :
      case LDC_I4_1 :
      case LDC_I4_2 :
      case LDC_I4_3 :
      case LDC_I4_4 :
      case LDC_I4_5 :
      case LDC_I4_6 :
      case LDC_I4_7 :
      case LDC_I4_8 :
      case LDC_I4_M1 : break;
      
      case LDC_I4_S : {
        i += 1;
        break;
      }
 
      case LDIND_U1 :
      case LDIND_I1 :
      case LDIND_U2 :
      case LDIND_I2 :
      case LDIND_U4 :
      case LDIND_I4 :
      case LDIND_I8 :
      case LDIND_R4 :
      case LDIND_R8 :
      case LDIND_I : break;
      
      case LDIND_REF : {
        break;
      }
 
      case LDLOC_S : {
        i += 1;
        break;
      }
      
      case LDLOC_0 :
      case LDLOC_1 :
      case LDLOC_2 :
      case LDLOC_3 : break;
      
      case LDLOCA_S : {
        i += 1;
        break;
      }
      
      case LDNULL : break;

      case LEAVE : {
        uint32 tmp = i;
        uint32 value = readS4(bytecodes, i);
        uint32 index = tmp + 5 + value;
        if (!(opcodeInfos[index].newBlock))
          opcodeInfos[index].newBlock = createBasicBlock("LEAVE");
        leaves.push_back(opcodeInfos[index].newBlock);
        break;
      }
      
      case LEAVE_S : {
        uint32 tmp = i;
        uint8 value = readS1(bytecodes, i);
        uint32 index = tmp + 2 + value;
        if (!(opcodeInfos[index].newBlock))
          opcodeInfos[index].newBlock = createBasicBlock("LEAVE_S");
        leaves.push_back(opcodeInfos[index].newBlock);
        break;
      }

      case MUL :
      case MUL_OVF :
      case MUL_OVF_UN :
      case NEG :
      case NOP :
      case NOT :
      case OR :
      case POP :
      case REM :
      case REM_UN :
      case RET :
      case SHL :
      case SHR :
      case SHR_UN : break;
      
      case STARG_S : {
        i += 1;
        break;
      }

      case STIND_I1 :
      case STIND_I2 :
      case STIND_I4 :
      case STIND_I8 :
      case STIND_R4 :
      case STIND_R8 :
      case STIND_I :
      case STIND_REF : break;
      
      case STLOC_S : {
        i += 1;
        break;
      }
      
      case STLOC_0 :
      case STLOC_1 :
      case STLOC_2 :
      case STLOC_3 :
      case SUB :
      case SUB_OVF :
      case SUB_OVF_UN : break;

      case SWITCH : {
        uint32 value = readU4(bytecodes, i);
        uint32 next = i + value * sizeof(sint32) + 1;
        for (uint32 t = 0; t < value; t++) {
          sint32 offset = readS4(bytecodes, i);
          sint32 index = next + offset;
          assert(index > 0);
          if (!(opcodeInfos[index].newBlock)) {
            BasicBlock* block = createBasicBlock("switch");
            opcodeInfos[index].newBlock = block;
          }
        }
        if (!(opcodeInfos[i + 1].newBlock)) {
          BasicBlock* block = createBasicBlock("switch");
          opcodeInfos[i + 1].newBlock = block;
        }
        break;
      }

      case XOR : break;

      case BOX : {
        i += 4;
        break; 
      }

      case CALLVIRT : {
        i += 4;
        break; 
      }

      case CASTCLASS : {
        i += 4;
        break;  
      }

      case CPOBJ : {
        VMThread::get()->getVM()->error("implement me");
        break;  
      }
       
      case ISINST : {
        i += 4;
        break;  
      }
      
      case LDELEM : {
        VMThread::get()->getVM()->error("implement me");
        break;  
      }
      
      case LDELEM_I1 : {
        VMThread::get()->getVM()->error("implement me");
        break;  
      }

      case LDELEM_I2 :
      case LDELEM_I4 :
      case LDELEM_I8 :
      case LDELEM_U1 :
      case LDELEM_U2 :
      case LDELEM_U4 :
      case LDELEM_R4 :
      case LDELEM_R8 :
      case LDELEM_I :
      case LDELEM_REF :
      case LDELEMA : break;

      case LDFLD : {
        i += 4;
        break;
      }
      
      case LDFLDA : {
        i += 4;
        break;
      }

      case LDLEN : break;

      case LDOBJ : {
        i += 4;
        break;
      }

      case LDSFLD : {
        i += 4;
        break;
      }
      
      case LDSFLDA : {
        i += 4;
        break;
      }

      case LDSTR : {
        i += 4;
        break;
      }

      case LDTOKEN : {
        i += 4;
        break;
      }
      
      case MKREFANY : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }

      case NEWARR : {
        i += 4;
        break;
      }

      case NEWOBJ : {
        i += 4;
        break;
      }
      
      case REFANYVAL : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }
      
      case STELEM : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }

      case STELEM_I1 :
      case STELEM_I2 :
      case STELEM_I4 :
      case STELEM_I8 :
      case STELEM_R4 :
      case STELEM_R8 :
      case STELEM_I :
      case STELEM_REF : break;

      case STFLD : {
        i += 4;
        break;
      }

      case STOBJ : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }

      case STSFLD : {
        i += 4;
        break;
      }

      case THROW : {
        break;
      }

      case UNBOX : {
        i += 4;
        break;
      }
      
      case UNBOX_ANY : {
        VMThread::get()->getVM()->error("implement me");
        break;
      }

      case 0xFE : {
      
        PRINT_DEBUG(N3_COMPILE, 1, COLOR_NORMAL, "\t[at %5x] %-5d ", i,
                    bytecodes[i + 1]);
        PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "exploring %s::", mvm::PrintBuffer(compilingMethod).cString());
        PRINT_DEBUG(N3_COMPILE, 1, LIGHT_CYAN, OpcodeNamesFE[bytecodes[i + 1]]);
        PRINT_DEBUG(N3_COMPILE, 1, LIGHT_BLUE, "\n");
      
        switch (bytecodes[++i]) {

          case CEQ:
          case CGT:
          case CGT_UN:
          case CLT:
          case CLT_UN:
          case CPBLK : break;
      
          case ENDFILTER: {
            VMThread::get()->getVM()->error("implement me");
            break;
          }
          
          case LDARG : {
            i += 2;
            break;
          }
          
          case LDARGA : {
            i += 2;
            break;
          }
          
          case LDFTN : {
            i += 4;
            break;
          }

          case INITBLK : break;
      
          case LDLOC : {
            i += 2;
            break;
          }
          
          case LOCALLOC : break;
          
          case STARG : {
            i += 2;
            break;
          }
          
          case STLOC : {
            i += 2;
            break;
          }
          
          case LDLOCA : {
            i += 2;
            break;
          }
      
          case ARGLIST : {
            VMThread::get()->getVM()->error("implement me");
            break;
          }
      
          case INITOBJ : {
            i += 4;
            break;  
          }
          
          case LDVIRTFTN : {
            VMThread::get()->getVM()->error("implement me");
            break;
          }
          
          case REFANYTYPE : {
            VMThread::get()->getVM()->error("implement me");
            break;
          }
          
          case RETHROW : {
            break;
          }
        
          case SIZEOF : {
            VMThread::get()->getVM()->error("implement me");
            break;
          }

          case VOLATILE_ : {
            break;
          }
          default :
            VMThread::get()->getVM()->unknownError("unknown bytecode");
        } 
        break;
      }
      
      default :
        VMThread::get()->getVM()->unknownError("unknown bytecode");
    }
  }
}
