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

#define JNJVM_COMPILE 2
#define JNJVM_EXECUTE 1


#include <cstring>
#include <queue>

#include <llvm/IR/Constants.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Type.h>

#include "vmkit/JIT.h"

#include "debug.h"

#include "JavaArray.h"
#include "JavaClass.h"
#include "JavaConstantPool.h"
#include "JavaObject.h"
#include "JavaJIT.h"
#include "JavaThread.h"
#include "JavaTypes.h"
#include "Jnjvm.h"
#include "Reader.h"

#include "j3/J3Intrinsics.h"

#include "j3/OpcodeNames.def"

using namespace j3;
using namespace llvm;

uint8 arrayType(JavaMethod* meth, unsigned int t) {
  if (t == JavaArray::T_CHAR) {
    return I_CHAR;
  } else if (t == JavaArray::T_BOOLEAN) {
    return I_BOOL;
  } else if (t == JavaArray::T_INT) {
    return I_INT;
  } else if (t == JavaArray::T_SHORT) {
    return I_SHORT;
  } else if (t == JavaArray::T_BYTE) {
    return I_BYTE;
  } else if (t == JavaArray::T_FLOAT) {
    return I_FLOAT;
  } else if (t == JavaArray::T_LONG) {
    return I_LONG;
  } else if (t == JavaArray::T_DOUBLE) {
    return I_DOUBLE;
  } else {
    fprintf(stderr, "I haven't verified your class file and it's malformed:"
                    " unknown array type %d in %s.%s!\n", t,
                    UTF8Buffer(meth->classDef->name).cString(),
                    UTF8Buffer(meth->name).cString());
    abort();
  }
}

static inline uint32 WREAD_U1(Reader& reader, bool init, uint32 &i, bool& wide) {
  if (wide) {
    wide = init;
    i += 2;
    return reader.readU2();
  } else {
    i += 1;
    return reader.readU1();
  }
}

static inline sint32 WREAD_S1(Reader& reader, bool init, uint32 &i, bool &wide) {
  if (wide) {
    wide = init; 
    i += 2;
    return reader.readS2();
  } else {
    i += 1;
    return reader.readS1();
  }
}

static inline uint32 WCALC(uint32 n, bool& wide) {
  if (wide) {
    wide = false;
    return n << 1;
  } else {
    return n;
  }
}

/**
 * Used in JavaJIT::findUnreachableCode to find the next instruction in the array of bytecode.
 */
static uint8 sum [] = {
		2,
		3,
		2,
		3,
		3,
		2,
		2,
		2,
		2,
		2
};
static uint8 sum2 [] = {
		3,
		3,
		3,
		3,
		3,
		3,
		3,
		5,
		5,
		3,
		2,
		3,
		1,
		1,
		3,
		3,
		1,
		1
};

void JavaJIT::findUnreachableCode(Reader& reader, uint32 codeLen) {
	std::queue<uint32> queue;
	queue.push(0);
	opcodeInfos[0].isReachable = true;

	for (unsigned i = 0; i < codeLen; ++i) {
		if (opcodeInfos[i].isReachable)
			queue.push(i);
	}

	uint32 start = reader.cursor;
	while (queue.size()) {
		// pick one index from the queue
		uint32 i_opcode = queue.front();
		queue.pop();
		// read the opcode at this position
		reader.cursor = start + i_opcode;
		uint8 bytecode = reader.readU1();
		// if the opcode is a section which has a handle other than the endBlockHandler then we must add the initial instruction of that handler to the queue
		if (opcodeInfos[i_opcode].handlerPC) {
			uint16 pc = opcodeInfos[i_opcode].handlerPC;
			if (pc < codeLen && !opcodeInfos[pc].isReachable) {
				opcodeInfos[pc].isReachable = true;
				queue.push(pc);
			}
		}
		//
		// if the opcode does not involve a branch then mark the next instruction as reachable (DONE)
		// if the opcode is an unconditional branch then mark the target as reachable (DONE)
		// if the opcode is a conditional branch then mark the target and the next instruction as reachable (DONE)
		// if it is a ret instruction then I don't know what to do.
		// if it is a jsr instruction then mark the target as reachable (DONE)
		// if it is tableswitch instruction then mark all cases as reachable (DONE)
		// if it is lookupswitch instruction then mark all cases as reachable (DONE)
		// wide instruction (DONE)
		if (bytecode < IFEQ) {
			uint32 next_index = 1;
			if (bytecode >= BIPUSH && bytecode <= ALOAD)
				next_index = sum[bytecode - BIPUSH];
			else if (bytecode >= ISTORE && bytecode <= ASTORE)
				next_index = 2;
			else if (bytecode == IINC)
				next_index = 3;
			next_index += i_opcode;
			if (next_index < codeLen && !opcodeInfos[next_index].isReachable) {
				opcodeInfos[next_index].isReachable = true;
				queue.push(next_index);
			}
		}
		else if (bytecode >= IFEQ && bytecode <= IF_ACMPNE) {
			uint32 next_index = 3 + i_opcode;
			if (next_index < codeLen && !opcodeInfos[next_index].isReachable) {
				opcodeInfos[next_index].isReachable = true;
				queue.push(next_index);
			}
			uint16 b1 = reader.readU1();
			uint8 b2 = reader.readU1();
			sint16 step = (b1 << 8) | b2;
			next_index = i_opcode + step;
			if (next_index < codeLen && !opcodeInfos[next_index].isReachable) {
				opcodeInfos[next_index].isReachable = true;
				queue.push(next_index);
			}
		}
		else if (bytecode >= GETSTATIC && bytecode <= MONITOREXIT && bytecode != ATHROW) {
			uint32 next_index = sum2[bytecode - GETSTATIC] + i_opcode;
			if (next_index < codeLen && !opcodeInfos[next_index].isReachable) {
				opcodeInfos[next_index].isReachable = true;
				queue.push(next_index);
			}
		}
		else {
			uint32 x;
			sint32 defaultIndex,lowIndex, highIndex;
			uint32 next_index = codeLen;
			sint16 step;
			sint32 step32;
			uint16 b1;
			uint8 b2,b3,b4;
			switch (bytecode) {
				case GOTO:
					step = reader.readS2();
					next_index = i_opcode + step;
					break;
				case GOTO_W:
					step32 =  reader.readS4();
					next_index = step32 + i_opcode;
					break;
				case MULTIANEWARRAY:
					next_index = i_opcode + 4;
					break;
				case IFNULL:
				case IFNONNULL:
				case JSR:
					next_index = i_opcode + 3;
					if (next_index < codeLen && !opcodeInfos[next_index].isReachable) {
						opcodeInfos[next_index].isReachable = true;
						queue.push(next_index);
					}
					step = reader.readS2();
					next_index = i_opcode + step;
					break;
				case JSR_W:
					next_index = i_opcode + 5;
					if (next_index < codeLen && !opcodeInfos[next_index].isReachable) {
						opcodeInfos[next_index].isReachable = true;
						queue.push(next_index);
					}
					step32 =  reader.readS4();
					next_index = step32 + i_opcode;
					break;
				case WIDE:
					if (i_opcode + 1 < codeLen && !opcodeInfos[i_opcode + 1].isReachable) {
						opcodeInfos[i_opcode + 1].isReachable = true;
						queue.push(i_opcode + 1);
					}
					b2 = reader.readU1();
					next_index = i_opcode + ((b2 == IINC)?6:4);
					break;
				case TABLESWITCH:
					//break;
					x = i_opcode & 0x03; // remainder of division by 4
					reader.cursor = start + 4 - x + i_opcode;
					// default
					step32 =  reader.readS4();
					defaultIndex = step32 + i_opcode;
					if (defaultIndex < codeLen && !opcodeInfos[defaultIndex].isReachable) {
						opcodeInfos[defaultIndex].isReachable = true;
						queue.push(defaultIndex);
					}
					// low
					lowIndex = reader.readS4();
					// high
					highIndex = reader.readS4();
					// all options
					for (int i = 0 ; i < highIndex - lowIndex + 1 ; i++) {
						step32 =  reader.readS4();
						next_index = step32 + i_opcode;
						if (next_index < codeLen && !opcodeInfos[next_index].isReachable) {
							opcodeInfos[next_index].isReachable = true;
							queue.push(next_index);
						}
					}
					next_index = codeLen;
					break;
				case LOOKUPSWITCH:
					//break;
					x = i_opcode & 0x03; // remainder of division by 4
					reader.cursor = start + 4 - x + i_opcode;
					// default
					step32 =  reader.readS4();//(defaultIndex << 24) | (b2 << 16) | (b3 << 8) | b4;
					defaultIndex = step32 + i_opcode;
					if (defaultIndex < codeLen && !opcodeInfos[defaultIndex].isReachable) {
						opcodeInfos[defaultIndex].isReachable = true;
						queue.push(defaultIndex);
					}
					// npairs
					lowIndex = reader.readS4();
					// all options
					for (int i = 0 ; i < lowIndex ; i++) {
						reader.cursor += 4; // skipping match
						// offset
						step32 =  reader.readS4();
						next_index = step32 + i_opcode;
						if (next_index < codeLen && !opcodeInfos[next_index].isReachable) {
							opcodeInfos[next_index].isReachable = true;
							queue.push(next_index);
						}
					}
					next_index = codeLen;
					break;
				default:
					break;
			}
			if (next_index < codeLen && !opcodeInfos[next_index].isReachable) {
				opcodeInfos[next_index].isReachable = true;
				queue.push(next_index);
			}
		}
	} // end while
}

void JavaJIT::compileOpcodes(Reader& reader, uint32 codeLength) {
  bool wide = false;
  uint32 jsrIndex = 0;
  uint32 start = reader.cursor;
  // Some compilers like Scala and Kotlin produce unreachable code.
  if (!opcodeInfos[0].isReachable) {
	  findUnreachableCode(reader, codeLength);
	  reader.cursor = start;
  }

  vmkit::ThreadAllocator allocator;
  for(uint32 i = 0; i < codeLength; ++i) {
	if (!opcodeInfos[i].isReachable && !opcodeInfos[i].handler) {
		continue;
	}
	reader.cursor = start + i;
    uint8 bytecode = reader.readU1();
    
    PRINT_DEBUG(JNJVM_COMPILE, 1, DARK_BLUE, "\t[at %5d] %-5d ", i,
                bytecode);
    PRINT_DEBUG(JNJVM_COMPILE, 1, LIGHT_BLUE, " compiling ");
    PRINT_DEBUG(JNJVM_COMPILE, 1, LIGHT_CYAN, OpcodeNames[bytecode]);
    PRINT_DEBUG(JNJVM_COMPILE, 1, LIGHT_BLUE, "\n");
    
    Opinfo* opinfo = &(opcodeInfos[i]);
    if (opinfo->newBlock) {
      if (currentBlock->getTerminator() == 0) {
        // Load the exception object if we have branched to a handler.
    	  bool b = false;
        if (opinfo->handler) {
          Instruction* I = opinfo->newBlock->begin();
          PHINode * node = dyn_cast<PHINode>(I);
          if (!node) {
        	  //llvmFunction->dump();
        	  //Value* obj = pop();
        	  //PHINode* node = PHINode::Create(obj->getType(), 0, "jaja", opinfo->newBlock);
        	  //node->addIncoming(obj, currentBlock);
        	  // if we were in a handler and now we are in a new handler we simulate a throw
        	  llvm::Value* arg = pop();
        	  throwException(arg);
        	  b = true;
        	  // Original Code
        	  //assert(node && "Handler marlformed");
          }
          else {

			  Value* obj = pop();
			  node->addIncoming(obj, currentBlock);
          }
        }
        if (!b)
        	branch(*opinfo, currentBlock);
      }
      
      currentBlock = opinfo->newBlock;
      
      stack.clear();
      if (opinfo->handler) {
        // If it's a handler, put the exception object in the stack.
        Value* javaExceptionPtr = getJavaExceptionPtr(getJavaThreadPtr(getMutatorThreadPtr()));
        Value* obj = new LoadInst(javaExceptionPtr, "pendingException", currentBlock);
        new StoreInst(obj, objectStack[0], "", currentBlock);
        // And clear the exception.
        new StoreInst(intrinsics->JavaObjectNullConstant, javaExceptionPtr, currentBlock);
        stack.push_back(MetaInfo(upcalls->OfObject, NOP));
        currentStackIndex = 1;
      } else {
        stack = opinfo->stack;
        currentStackIndex = stack.size();
      }

      if (opinfo->backEdge) {
        checkYieldPoint();
      }
    }

    currentExceptionBlock = opinfo->exceptionBlock;
    
    currentBytecodeIndex = i;
    currentBytecode = bytecode;

    // To prevent a gcj bug with useless goto
    if (currentBlock->getTerminator() != 0) { 
      currentBlock = createBasicBlock("gcj bug");
    }
    
#if JNJVM_EXECUTE > 1
    {
      Value* args[3] = {
        ConstantInt::get(Type::getInt32Ty(*llvmContext), (int64_t)bytecode),
        ConstantInt::get(Type::getInt32Ty(*llvmContext), (int64_t)i),
        TheCompiler->getMethodInClass(compilingMethod)
      };
    
    
      CallInst::Create(intrinsics->PrintExecutionFunction, args, "",
                       currentBlock);
    }
#endif
    
    switch (bytecode) {
      
      case NOP : break;

      case ACONST_NULL : 
        push(intrinsics->JavaObjectNullConstant, false);
        break;

      case ICONST_M1 :
        push(intrinsics->constantMinusOne, false);
        break;

      case ICONST_0 :
        push(intrinsics->constantZero, false);
        break;

      case ICONST_1 :
        push(intrinsics->constantOne, false);
        break;

      case ICONST_2 :
        push(intrinsics->constantTwo, false);
        break;

      case ICONST_3 :
        push(intrinsics->constantThree, false);
        break;

      case ICONST_4 :
        push(intrinsics->constantFour, false);
        break;

      case ICONST_5 :
        push(intrinsics->constantFive, false);
        break;

      case LCONST_0 :
        push(intrinsics->constantLongZero, false);
        push(intrinsics->constantZero, false);
        break;

      case LCONST_1 :
        push(intrinsics->constantLongOne, false);
        push(intrinsics->constantZero, false);
        break;

      case FCONST_0 :
        push(intrinsics->constantFloatZero, false);
        break;

      case FCONST_1 :
        push(intrinsics->constantFloatOne, false);
        break;
      
      case FCONST_2 :
        push(intrinsics->constantFloatTwo, false);
        break;
      
      case DCONST_0 :
        push(intrinsics->constantDoubleZero, false);
        push(intrinsics->constantZero, false);
        break;
      
      case DCONST_1 :
        push(intrinsics->constantDoubleOne, false);
        push(intrinsics->constantZero, false);
        break;

      case BIPUSH : 
        push(ConstantExpr::getSExt(ConstantInt::get(Type::getInt8Ty(*llvmContext),
                                                    reader.readU1()),
                                   Type::getInt32Ty(*llvmContext)), false);
        i++;
        break;

      case SIPUSH :
        push(ConstantExpr::getSExt(ConstantInt::get(Type::getInt16Ty(*llvmContext),
                                                    reader.readS2()),
                                   Type::getInt32Ty(*llvmContext)), false);
        i += 2;
        break;

      case LDC :
        loadConstant(reader.readU1());
        i++;
        break;

      case LDC_W :
        loadConstant(reader.readS2());
        i += 2;
        break;

      case LDC2_W :
        loadConstant(reader.readS2());
        i += 2;
        push(intrinsics->constantZero, false);
        break;

      case ILOAD :
        push(new LoadInst(intLocals[WREAD_U1(reader, false, i, wide)], "", false,
                          currentBlock), false);
        break;

      case LLOAD :
        push(new LoadInst(longLocals[WREAD_U1(reader, false, i, wide)], "", false,
                          currentBlock), false);
        push(intrinsics->constantZero, false);
        break;

      case FLOAD :
        push(new LoadInst(floatLocals[WREAD_U1(reader, false, i, wide)], "", false,
                          currentBlock), false);
        break;

      case DLOAD :
        push(new LoadInst(doubleLocals[WREAD_U1(reader, false, i, wide)], "", false,
                          currentBlock), false);
        push(intrinsics->constantZero, false);
        break;

      case ALOAD :
        push(new LoadInst(objectLocals[WREAD_U1(reader, false, i, wide)], "",
                          false, currentBlock), false);
        break;
      
      case ILOAD_0 :
        push(new LoadInst(intLocals[0], "", false, currentBlock), false);
        break;
      
      case ILOAD_1 :
        push(new LoadInst(intLocals[1], "", false, currentBlock), false);
        break;

      case ILOAD_2 :
        push(new LoadInst(intLocals[2], "", false, currentBlock), false);
        break;

      case ILOAD_3 :
        push(new LoadInst(intLocals[3], "", false, currentBlock), false);
        break;
      
      case LLOAD_0 :
        push(new LoadInst(longLocals[0], "", false, currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;

      case LLOAD_1 :
        push(new LoadInst(longLocals[1], "", false, currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      
      case LLOAD_2 :
        push(new LoadInst(longLocals[2], "", false, currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      
      case LLOAD_3 :
        push(new LoadInst(longLocals[3], "", false, currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      
      case FLOAD_0 :
        push(new LoadInst(floatLocals[0], "", false, currentBlock),
             false);
        break;
      
      case FLOAD_1 :
        push(new LoadInst(floatLocals[1], "", false, currentBlock),
             false);
        break;

      case FLOAD_2 :
        push(new LoadInst(floatLocals[2], "", false, currentBlock),
             false);
        break;

      case FLOAD_3 :
        push(new LoadInst(floatLocals[3], "", false, currentBlock),
             false);
        break;
      
      case DLOAD_0 :
        push(new LoadInst(doubleLocals[0], "", false, currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;

      case DLOAD_1 :
        push(new LoadInst(doubleLocals[1], "", false, currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      
      case DLOAD_2 :
        push(new LoadInst(doubleLocals[2], "", false, currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      
      case DLOAD_3 :
        push(new LoadInst(doubleLocals[3], "", false, currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      
      case ALOAD_0 :
        push(new LoadInst(objectLocals[0], "", false, currentBlock),
             false);
        break;
      
      case ALOAD_1 :
        push(new LoadInst(objectLocals[1], "", false, currentBlock),
             false);
        break;

      case ALOAD_2 :
        push(new LoadInst(objectLocals[2], "", false, currentBlock),
             false);
        break;

      case ALOAD_3 :
        push(new LoadInst(objectLocals[3], "", false, currentBlock),
             false);
        break;
      
      case IALOAD : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index, 
                                         intrinsics->JavaArraySInt32Type);
        push(new LoadInst(ptr, "", currentBlock), false);
        break;
      }

      case LALOAD : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArrayLongType);
        push(new LoadInst(ptr, "", currentBlock), false);
        push(intrinsics->constantZero, false);
        break;
      }

      case FALOAD : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArrayFloatType);
        push(new LoadInst(ptr, "", currentBlock), false);
        break;
      }

      case DALOAD : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArrayDoubleType);
        push(new LoadInst(ptr, "", currentBlock), false);
        push(intrinsics->constantZero, false);
        break;
      }

      case AALOAD : {
        Value* index = pop();
        CommonClass* cl = topTypeInfo();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArrayObjectType);
        
        if (cl->isArray()) cl = cl->asArrayClass()->baseClass();
        push(new LoadInst(ptr, "", currentBlock), false, cl);
        break;
      }

      case BALOAD : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArraySInt8Type);
        Value* val = new LoadInst(ptr, "", currentBlock);
        push(new SExtInst(val, Type::getInt32Ty(*llvmContext), "", currentBlock),
             false);
        break;
      }

      case CALOAD : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArrayUInt16Type);
        Value* val = new LoadInst(ptr, "", currentBlock);
        push(new ZExtInst(val, Type::getInt32Ty(*llvmContext), "", currentBlock),
             false);
        break;
      }

      case SALOAD : {
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArraySInt16Type);
        Value* val = new LoadInst(ptr, "", currentBlock);
        push(new SExtInst(val, Type::getInt32Ty(*llvmContext), "", currentBlock),
             false);
        break;
      }

      case ISTORE : {
        Value* val = popAsInt();
        new StoreInst(val, intLocals[WREAD_U1(reader, false, i, wide)],
                      false, currentBlock);
        break;
      }
      
      case LSTORE :
        pop(); // remove the 0 on the stack
        new StoreInst(pop(), longLocals[WREAD_U1(reader, false, i, wide)],
                      false, currentBlock);
        break;
      
      case FSTORE :
        new StoreInst(pop(), floatLocals[WREAD_U1(reader, false, i, wide)],
                      false, currentBlock);
        break;
      
      case DSTORE :
        pop(); // remove the 0 on the stack
        new StoreInst(pop(), doubleLocals[WREAD_U1(reader, false, i, wide)],
                      false, currentBlock);
        break;

      case ASTORE : {
        CommonClass* cl = topTypeInfo();
        Instruction* V =
          new StoreInst(pop(), objectLocals[WREAD_U1(reader, false, i, wide)],
                        false, currentBlock);
        addHighLevelType(V, cl);
        break;
      } 
      case ISTORE_0 : {
        Value* val = pop();
        if (val->getType() != Type::getInt32Ty(*llvmContext)) // int8 and int16
          val = new ZExtInst(val, Type::getInt32Ty(*llvmContext), "", currentBlock);
        new StoreInst(val, intLocals[0], false, currentBlock);
        break;
      }
      
      case ISTORE_1 : {
        Value* val = pop();
        if (val->getType() != Type::getInt32Ty(*llvmContext)) // int8 and int16
          val = new ZExtInst(val, Type::getInt32Ty(*llvmContext), "", currentBlock);
        new StoreInst(val, intLocals[1], false, currentBlock);
        break;
      }

      case ISTORE_2 : {
        Value* val = pop();
        if (val->getType() != Type::getInt32Ty(*llvmContext)) // int8 and int16
          val = new ZExtInst(val, Type::getInt32Ty(*llvmContext), "", currentBlock);
        new StoreInst(val, intLocals[2], false, currentBlock);
        break;
      }

      case ISTORE_3 : {
        Value* val = pop();
        if (val->getType() != Type::getInt32Ty(*llvmContext)) // int8 and int16
          val = new ZExtInst(val, Type::getInt32Ty(*llvmContext), "", currentBlock);
        new StoreInst(val, intLocals[3], false, currentBlock);
        break;
      }

      case LSTORE_0 :
        pop(); // remove the 0 on the stack
        new StoreInst(pop(), longLocals[0], false, currentBlock);
        break;
      
      case LSTORE_1 :
        pop(); // remove the 0 on the stack
        new StoreInst(pop(), longLocals[1], false, currentBlock);
        break;
      
      case LSTORE_2 :
        pop(); // remove the 0 on the stack
        new StoreInst(pop(), longLocals[2], false, currentBlock);
        break;
      
      case LSTORE_3 :
        pop(); // remove the 0 on the stack
        new StoreInst(pop(), longLocals[3], false, currentBlock);
        break;
      
      case FSTORE_0 :
        new StoreInst(pop(), floatLocals[0], false, currentBlock);
        break;
      
      case FSTORE_1 :
        new StoreInst(pop(), floatLocals[1], false, currentBlock);
        break;
      
      case FSTORE_2 :
        new StoreInst(pop(), floatLocals[2], false, currentBlock);
        break;
      
      case FSTORE_3 :
        new StoreInst(pop(), floatLocals[3], false, currentBlock);
        break;
      
      case DSTORE_0 :
        pop(); // remove the 0 on the stack
        new StoreInst(pop(), doubleLocals[0], false, currentBlock);
        break;
      
      case DSTORE_1 :
        pop(); // remove the 0 on the stack
        new StoreInst(pop(), doubleLocals[1], false, currentBlock);
        break;
      
      case DSTORE_2 :
        pop(); // remove the 0 on the stack
        new StoreInst(pop(), doubleLocals[2], false, currentBlock);
        break;
      
      case DSTORE_3 :
        pop(); // remove the 0 on the stack
        new StoreInst(pop(), doubleLocals[3], false, currentBlock);
        break;
      
      case ASTORE_0 : {
        CommonClass* cl = topTypeInfo();
        Instruction* V = new StoreInst(pop(), objectLocals[0], false,
                                       currentBlock);
        addHighLevelType(V, cl);
        break;
      }
      
      case ASTORE_1 : {
        CommonClass* cl = topTypeInfo();
        Instruction* V = new StoreInst(pop(), objectLocals[1], false,
                                       currentBlock);
        addHighLevelType(V, cl);
        break;
      }
      
      case ASTORE_2 : {
        CommonClass* cl = topTypeInfo();
        Instruction* V = new StoreInst(pop(), objectLocals[2], false,
                                       currentBlock);
        addHighLevelType(V, cl);
        break;
      }
      
      case ASTORE_3 : {
        CommonClass* cl = topTypeInfo();
        Instruction* V = new StoreInst(pop(), objectLocals[3], false,
                                       currentBlock);
        addHighLevelType(V, cl);
        break;
      }
      
      case IASTORE : {
        Value* val = popAsInt();
        Value* index = popAsInt();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArraySInt32Type);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }

      case LASTORE : {
        pop(); // remove the 0 on stack
        Value* val = pop();
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArrayLongType);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }

      case FASTORE : {
        Value* val = pop();
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArrayFloatType);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }
      
      case DASTORE : {
        pop(); // remove the 0 on stack
        Value* val = pop();
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArrayDoubleType);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }
      
      case AASTORE : {
        if (TheCompiler->hasExceptionsEnabled()) {
          // Get val and object and don't pop them: IsSubclassOfFunction
          // may go into runtime and we don't want values in registers at that
          // point.
          Value* val = new LoadInst(objectStack[currentStackIndex - 1], "",
                                    false,
                                    currentBlock);
          Value* obj = new LoadInst(objectStack[currentStackIndex - 3], "",
                                    false,
                                    currentBlock);
          JITVerifyNull(obj);



          Value* res = 0;
          if (TheCompiler->isStaticCompiling()) {
        	  BasicBlock* endBlock = createBasicBlock("end array store check");
        	  BasicBlock* checkBlock = createBasicBlock("array store check");
        	  BasicBlock* exceptionBlock =
        	          		  createBasicBlock("array store exception");
        	  Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val,
                                    intrinsics->JavaObjectNullConstant, "");


          	  BranchInst::Create(endBlock, checkBlock, cmp, currentBlock);
          	  	  	  currentBlock = checkBlock;
        
          	  Value* valVT = CallInst::Create(intrinsics->GetVTFunction, val, "",
                                          currentBlock);
         
          	  Value* objVT = CallInst::Create(intrinsics->GetVTFunction, obj, "",
                                          currentBlock);
          	  objVT = CallInst::Create(intrinsics->GetBaseClassVTFromVTFunction, objVT,
                                   "", currentBlock);
          
          	  Value* VTArgs[2] = { valVT, objVT };
          
          	  res = CallInst::Create(
        		  (TheCompiler->isStaticCompiling())?
        				  intrinsics->IsSubclassOfFunction:
        				  intrinsics->IsSubclassOfFunctionInner,
                                        VTArgs, "", currentBlock);
          	BranchInst::Create(endBlock, exceptionBlock, res, currentBlock);

          	currentBlock = exceptionBlock;
          	throwRuntimeException(intrinsics->ArrayStoreExceptionFunction, VTArgs, 2);
          	currentBlock = endBlock;
          }
          else {
        	  Value* objects[2] = {val, obj};
        	  res = CallInst::Create(intrinsics->CheckIfAssignable, objects, "", currentBlock);
          }

          
        }
        Value* val = pop();
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArrayObjectType, false);
        if (vmkit::Collector::needsWriteBarrier()) {
          ptr = new BitCastInst(ptr, intrinsics->ptrPtrType, "", currentBlock);
          val = new BitCastInst(val, intrinsics->ptrType, "", currentBlock);
          obj = new BitCastInst(obj, intrinsics->ptrType, "", currentBlock);
          Value* args[3] = { obj, ptr, val };
          CallInst::Create(intrinsics->ArrayWriteBarrierFunction, args, "", currentBlock);
        } else {
          new StoreInst(val, ptr, false, currentBlock);
        }
        break;
      }

      case BASTORE : {
        Value* val = pop();
        if (val->getType() != Type::getInt8Ty(*llvmContext)) {
          val = new TruncInst(val, Type::getInt8Ty(*llvmContext), "", currentBlock);
        }
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArraySInt8Type);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }

      case CASTORE : {
        Value* val = pop();
        Type* type = val->getType();
        if (type == Type::getInt32Ty(*llvmContext)) {
          val = new TruncInst(val, Type::getInt16Ty(*llvmContext), "", currentBlock);
        } else if (type == Type::getInt8Ty(*llvmContext)) {
          val = new ZExtInst(val, Type::getInt16Ty(*llvmContext), "", currentBlock);
        }
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArrayUInt16Type);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }

      case SASTORE : {
        Value* val = pop();
        Type* type = val->getType();
        if (type == Type::getInt32Ty(*llvmContext)) {
          val = new TruncInst(val, Type::getInt16Ty(*llvmContext), "", currentBlock);
        } else if (type == Type::getInt8Ty(*llvmContext)) {
          val = new SExtInst(val, Type::getInt16Ty(*llvmContext), "", currentBlock);
        }
        Value* index = pop();
        Value* obj = pop();
        Value* ptr = verifyAndComputePtr(obj, index,
                                         intrinsics->JavaArraySInt16Type);
        new StoreInst(val, ptr, false, currentBlock);
        break;
      }

      case POP :
        pop();
        break;

      case POP2 :
        pop(); pop();
        break;

      case DUP :
        // TODO: The following bytecodes should push a MetaInfo.
        push(top(), false, topTypeInfo());
        break;

      case DUP_X1 : {
        CommonClass* oneCl = topTypeInfo();
        Value* one = pop();
        CommonClass* twoCl = topTypeInfo();
        Value* two = pop();
        
        push(one, false, oneCl);
        push(two, false, twoCl);
        push(one, false, oneCl);
        break;
      }

      case DUP_X2 : {
        CommonClass* oneCl = topTypeInfo();
        Value* one = pop();
        CommonClass* twoCl = topTypeInfo();
        Value* two = pop();
        CommonClass* threeCl = topTypeInfo();
        Value* three = pop();

        push(one, false, oneCl);
        push(three, false, threeCl);
        push(two, false, twoCl);
        push(one, false, oneCl);
        break;
      }

      case DUP2 : {
        CommonClass* oneCl = topTypeInfo();
        Value* one = pop();
        CommonClass* twoCl = topTypeInfo();
        Value* two = pop();
        
        push(two, false, twoCl);
        push(one, false, oneCl);
        push(two, false, twoCl);
        push(one, false, oneCl);
        break;
      }

      case DUP2_X1 : {
        CommonClass* oneCl = topTypeInfo();
        Value* one = pop();
        CommonClass* twoCl = topTypeInfo();
        Value* two = pop();
        CommonClass* threeCl = topTypeInfo();
        Value* three = pop();

        push(two, false, twoCl);
        push(one, false, oneCl);

        push(three, false, threeCl);
        push(two, false, twoCl);
        push(one, false, oneCl);

        break;
      }

      case DUP2_X2 : {
        CommonClass* oneCl = topTypeInfo();
        Value* one = pop();
        CommonClass* twoCl = topTypeInfo();
        Value* two = pop();
        CommonClass* threeCl = topTypeInfo();
        Value* three = pop();
        CommonClass* fourCl = topTypeInfo();
        Value* four = pop();

        push(two, false, twoCl);
        push(one, false, oneCl);
        
        push(four, false, fourCl);
        push(three, false, threeCl);
        push(two, false, twoCl);
        push(one, false, oneCl);

        break;
      }

      case SWAP : {
        CommonClass* oneCl = topTypeInfo();
        Value* one = pop();
        CommonClass* twoCl = topTypeInfo();
        Value* two = pop();
        push(one, false, oneCl);
        push(two, false, twoCl);
        break;
      }

      case IADD : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        push(BinaryOperator::CreateAdd(val1, val2, "", currentBlock),
        			false);
        break;
      }

      case LADD : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        push(BinaryOperator::CreateAdd(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case FADD : {
        Value* val2 = pop();
        Value* val1 = pop();
        push(BinaryOperator::CreateFAdd(val1, val2, "", currentBlock),
             false);
        break;
      }

      case DADD : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        push(BinaryOperator::CreateFAdd(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case ISUB : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        push(BinaryOperator::CreateSub(val1, val2, "", currentBlock),
             false);
        break;
      }
      case LSUB : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        push(BinaryOperator::CreateSub(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case FSUB : {
        Value* val2 = pop();
        Value* val1 = pop();
        push(BinaryOperator::CreateFSub(val1, val2, "", currentBlock),
             false);
        break;
      }

      case DSUB : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        push(BinaryOperator::CreateFSub(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case IMUL : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        push(BinaryOperator::CreateMul(val1, val2, "", currentBlock),
             false);
        break;
      }

      case LMUL : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        push(BinaryOperator::CreateMul(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case FMUL : {
        Value* val2 = pop();
        Value* val1 = pop();
        push(BinaryOperator::CreateFMul(val1, val2, "", currentBlock),
             false);
        break;
      }

      case DMUL : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        push(BinaryOperator::CreateFMul(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case IDIV : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        if (TheCompiler->hasExceptionsEnabled()) {
          Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val2,
                                    intrinsics->constantZero, "");
          BasicBlock* ifFalse = createBasicBlock("non null div");
          BasicBlock* ifTrue = createBasicBlock("null div");

          BranchInst::Create(ifTrue, ifFalse, cmp, currentBlock);
          currentBlock = ifTrue;
          throwRuntimeException(intrinsics->ArithmeticExceptionFunction, 0, 0);
          currentBlock = ifFalse;  
        }
        Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val2,
                                  intrinsics->constantMinusOne, "");
        BasicBlock* ifFalse = createBasicBlock("non -1 div");
        BasicBlock* ifTrue = createBasicBlock("-1 div");
        BasicBlock* endBlock = createBasicBlock("End division");
        PHINode* node = PHINode::Create(val1->getType(), 2, "", endBlock);
        BranchInst::Create(ifTrue, ifFalse, cmp, currentBlock);
        currentBlock = ifTrue;
        node->addIncoming(BinaryOperator::CreateSub(intrinsics->constantZero,
                                                    val1, "", currentBlock),
                          currentBlock);
        BranchInst::Create(endBlock, currentBlock);
        currentBlock = ifFalse;
        node->addIncoming(
            BinaryOperator::CreateSDiv(val1, val2, "", currentBlock),
            currentBlock);
        BranchInst::Create(endBlock, currentBlock);
        currentBlock = endBlock;
        push(node, false);
        break;
      }

      case LDIV : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        if (TheCompiler->hasExceptionsEnabled()) {
          Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val2,
                                    intrinsics->constantLongZero, "");
          BasicBlock* ifFalse = createBasicBlock("non null div");
          BasicBlock* ifTrue = createBasicBlock("null div");

          BranchInst::Create(ifTrue, ifFalse, cmp, currentBlock);
          currentBlock = ifTrue;
          throwRuntimeException(intrinsics->ArithmeticExceptionFunction, 0, 0);
          currentBlock = ifFalse;
        }
        push(BinaryOperator::CreateSDiv(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case FDIV : {
        Value* val2 = pop();
        Value* val1 = pop();
        push(BinaryOperator::CreateFDiv(val1, val2, "", currentBlock),
             false);
        break;
      }

      case DDIV : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        push(BinaryOperator::CreateFDiv(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case IREM : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        if (TheCompiler->hasExceptionsEnabled()) {
          Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val2,
                                    intrinsics->constantZero, "");
          BasicBlock* ifFalse = createBasicBlock("non null rem");
          BasicBlock* ifTrue = createBasicBlock("null rem");

          BranchInst::Create(ifTrue, ifFalse, cmp, currentBlock);
          currentBlock = ifTrue;
          throwRuntimeException(intrinsics->ArithmeticExceptionFunction, 0, 0);
          currentBlock = ifFalse;
        }
        Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val2,
                                  intrinsics->constantMinusOne, "");
        BasicBlock* ifFalse = createBasicBlock("non -1 rem");
        BasicBlock* endBlock = createBasicBlock("end block");
        PHINode* node = PHINode::Create(val1->getType(), 2, "", endBlock);
        node->addIncoming(intrinsics->constantZero, currentBlock);
        BranchInst::Create(endBlock, ifFalse, cmp, currentBlock);
        currentBlock = ifFalse;
        node->addIncoming(
            BinaryOperator::CreateSRem(val1, val2, "", currentBlock),
            currentBlock);
        BranchInst::Create(endBlock, currentBlock);
        currentBlock = endBlock;
        push(node, false);
        break;
      }

      case LREM : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        if (TheCompiler->hasExceptionsEnabled()) {
          Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val2,
                                    intrinsics->constantLongZero, "");
          BasicBlock* ifFalse = createBasicBlock("non null div");
          BasicBlock* ifTrue = createBasicBlock("null div");

          BranchInst::Create(ifTrue, ifFalse, cmp, currentBlock);
          currentBlock = ifTrue;
          throwRuntimeException(intrinsics->ArithmeticExceptionFunction, 0, 0);
          currentBlock = ifFalse;
        }
        push(BinaryOperator::CreateSRem(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case FREM : {
        Value* val2 = pop();
        Value* val1 = pop();
        push(BinaryOperator::CreateFRem(val1, val2, "", currentBlock),
             false);
        break;
      }

      case DREM : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        push(BinaryOperator::CreateFRem(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case INEG :
        push(BinaryOperator::CreateSub(
                              intrinsics->constantZero,
                              popAsInt(), "", currentBlock),
             false);
        break;
      
      case LNEG : {
        pop();
        push(BinaryOperator::CreateSub(
                              intrinsics->constantLongZero,
                              pop(), "", currentBlock), false);
        push(intrinsics->constantZero, false);
        break;
      }

      case FNEG :
        push(BinaryOperator::CreateFSub(
                              intrinsics->constantFloatMinusZero,
                              pop(), "", currentBlock), false);
        break;
      
      case DNEG : {
        pop();
        push(BinaryOperator::CreateFSub(
                              intrinsics->constantDoubleMinusZero,
                              pop(), "", currentBlock), false);
        push(intrinsics->constantZero, false);
        break;
      }

      case ISHL : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        Value* mask = ConstantInt::get(Type::getInt32Ty(*llvmContext), 0x1F);
        val2 = BinaryOperator::CreateAnd(val2, mask, "", currentBlock);
        push(BinaryOperator::CreateShl(val1, val2, "", currentBlock),
             false);
        break;
      }

      case LSHL : {
        Value* val2 = new ZExtInst(pop(), Type::getInt64Ty(*llvmContext), "", currentBlock);
        Value* mask = ConstantInt::get(Type::getInt64Ty(*llvmContext), 0x3F);
        val2 = BinaryOperator::CreateAnd(val2, mask, "", currentBlock);
        pop(); // remove the 0 on the stack
        Value* val1 = pop();
        push(BinaryOperator::CreateShl(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case ISHR : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        Value* mask = ConstantInt::get(Type::getInt32Ty(*llvmContext), 0x1F);
        val2 = BinaryOperator::CreateAnd(val2, mask, "", currentBlock);
        push(BinaryOperator::CreateAShr(val1, val2, "", currentBlock),
             false);
        break;
      }

      case LSHR : {
        Value* val2 = new ZExtInst(pop(), Type::getInt64Ty(*llvmContext), "", currentBlock);
        Value* mask = ConstantInt::get(Type::getInt64Ty(*llvmContext), 0x3F);
        val2 = BinaryOperator::CreateAnd(val2, mask, "", currentBlock);
        pop(); // remove the 0 on the stack
        Value* val1 = pop();
        push(BinaryOperator::CreateAShr(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case IUSHR : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        Value* mask = ConstantInt::get(Type::getInt32Ty(*llvmContext), 0x1F);
        val2 = BinaryOperator::CreateAnd(val2, mask, "", currentBlock);
        push(BinaryOperator::CreateLShr(val1, val2, "", currentBlock),
             false);
        break;
      }

      case LUSHR : {
        Value* val2 = new ZExtInst(pop(), Type::getInt64Ty(*llvmContext), "", currentBlock);
        Value* mask = ConstantInt::get(Type::getInt64Ty(*llvmContext), 0x3F);
        val2 = BinaryOperator::CreateAnd(val2, mask, "", currentBlock);
        pop(); // remove the 0 on the stack
        Value* val1 = pop();
        push(BinaryOperator::CreateLShr(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case IAND : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        push(BinaryOperator::CreateAnd(val1, val2, "", currentBlock),
             false);
        break;
      }

      case LAND : {
        pop();
        Value* val2 = pop();
        pop(); // remove the 0 on the stack
        Value* val1 = pop();
        push(BinaryOperator::CreateAnd(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case IOR : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        push(BinaryOperator::CreateOr(val1, val2, "", currentBlock),
             false);
        break;
      }

      case LOR : {
        pop();
        Value* val2 = pop();
        pop(); // remove the 0 on the stack
        Value* val1 = pop();
        push(BinaryOperator::CreateOr(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case IXOR : {
        Value* val2 = popAsInt();
        Value* val1 = popAsInt();
        push(BinaryOperator::CreateXor(val1, val2, "", currentBlock),
             false);
        break;
      }

      case LXOR : {
        pop();
        Value* val2 = pop();
        pop(); // remove the 0 on the stack
        Value* val1 = pop();
        push(BinaryOperator::CreateXor(val1, val2, "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      }

      case IINC : {
        uint16 idx = WREAD_U1(reader, true, i, wide);
        sint16 val = WREAD_S1(reader, false, i, wide);
        llvm::Value* add = BinaryOperator::CreateAdd(
            new LoadInst(intLocals[idx], "", currentBlock), 
            ConstantInt::get(Type::getInt32Ty(*llvmContext), val), "",
            currentBlock);
        new StoreInst(add, intLocals[idx], false, currentBlock);
        break;
      }

      case I2L :
        push(new SExtInst(pop(), llvm::Type::getInt64Ty(*llvmContext), "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;

      case I2F :
        push(new SIToFPInst(pop(), llvm::Type::getFloatTy(*llvmContext), "", currentBlock),
             false);
        break;
        
      case I2D :
        push(new SIToFPInst(pop(), llvm::Type::getDoubleTy(*llvmContext), "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      
      case L2I :
        pop();
        push(new TruncInst(pop(), llvm::Type::getInt32Ty(*llvmContext), "", currentBlock),
             false);
        break;
      
      case L2F :
        pop();
        push(new SIToFPInst(pop(), llvm::Type::getFloatTy(*llvmContext), "", currentBlock),
             false);
        break;
      
      case L2D :
        pop();
        push(new SIToFPInst(pop(), llvm::Type::getDoubleTy(*llvmContext), "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      
      case F2I : {
        llvm::Value* val = pop();
        llvm::Value* test = new FCmpInst(*currentBlock, FCmpInst::FCMP_ONE,
                                         val, val, "");
        
        BasicBlock* res = createBasicBlock("F2I");
        PHINode* node = PHINode::Create(llvm::Type::getInt32Ty(*llvmContext), 4, "", res);
        node->addIncoming(intrinsics->constantZero, currentBlock);
        BasicBlock* cont = createBasicBlock("F2I");

        BranchInst::Create(res, cont, test, currentBlock);

        currentBlock = cont;
        
        test = new FCmpInst(*currentBlock, FCmpInst::FCMP_OGE, val, 
                            intrinsics->constantMaxIntFloat, "");

        cont = createBasicBlock("F2I");

        BranchInst::Create(res, cont, test, currentBlock);
        node->addIncoming(intrinsics->constantMaxInt,
                          currentBlock);

        currentBlock = cont;

        test = new FCmpInst(*currentBlock, FCmpInst::FCMP_OLE, val,
                            intrinsics->constantMinIntFloat, "");
        
        cont = createBasicBlock("F2I");

        BranchInst::Create(res, cont, test, currentBlock);
        node->addIncoming(intrinsics->constantMinInt, currentBlock);
        
        currentBlock = cont;
        llvm::Value* newVal = new FPToSIInst(val, Type::getInt32Ty(*llvmContext), "",
                                             currentBlock);
        BranchInst::Create(res, currentBlock);

        node->addIncoming(newVal, currentBlock);

        currentBlock = res;

        push(node, false);
        break;
      }

      case F2L : {
        llvm::Value* val = pop();
        llvm::Value* test = new FCmpInst(*currentBlock, FCmpInst::FCMP_ONE,
                                         val, val, "");
        
        BasicBlock* res = createBasicBlock("F2L");
        PHINode* node = PHINode::Create(Type::getInt64Ty(*llvmContext), 4, "", res);
        node->addIncoming(intrinsics->constantLongZero, currentBlock);
        BasicBlock* cont = createBasicBlock("F2L");

        BranchInst::Create(res, cont, test, currentBlock);

        currentBlock = cont;
        
        test = new FCmpInst(*currentBlock, FCmpInst::FCMP_OGE, val, 
                            intrinsics->constantMaxLongFloat, "");

        cont = createBasicBlock("F2L");

        BranchInst::Create(res, cont, test, currentBlock);
        node->addIncoming(intrinsics->constantMaxLong, currentBlock);

        currentBlock = cont;

        test = new FCmpInst(*currentBlock, FCmpInst::FCMP_OLE, val, 
                            intrinsics->constantMinLongFloat, "");
        
        cont = createBasicBlock("F2L");

        BranchInst::Create(res, cont, test, currentBlock);
        node->addIncoming(intrinsics->constantMinLong, currentBlock);
        
        currentBlock = cont;
        llvm::Value* newVal = new FPToSIInst(val, Type::getInt64Ty(*llvmContext), "",
                                             currentBlock);
        BranchInst::Create(res, currentBlock);

        node->addIncoming(newVal, currentBlock);

        currentBlock = res;
        
        push(node, false);
        push(intrinsics->constantZero, false);
        break;
      }

      case F2D :
        push(new FPExtInst(pop(), llvm::Type::getDoubleTy(*llvmContext), "", currentBlock),
             false);
        push(intrinsics->constantZero, false);
        break;
      
      case D2I : {
        pop(); // remove the 0 on the stack
        llvm::Value* val = pop();
        llvm::Value* test = new FCmpInst(*currentBlock, FCmpInst::FCMP_ONE,
                                         val, val, "");
        
        BasicBlock* res = createBasicBlock("D2I");
        PHINode* node = PHINode::Create(Type::getInt32Ty(*llvmContext), 4, "", res);
        node->addIncoming(intrinsics->constantZero, currentBlock);
        BasicBlock* cont = createBasicBlock("D2I");

        BranchInst::Create(res, cont, test, currentBlock);

        currentBlock = cont;
        
        test = new FCmpInst(*currentBlock, FCmpInst::FCMP_OGE, val,
                            intrinsics->constantMaxIntDouble, "");

        cont = createBasicBlock("D2I");

        BranchInst::Create(res, cont, test, currentBlock);
        node->addIncoming(intrinsics->constantMaxInt, currentBlock);

        currentBlock = cont;

        test = new FCmpInst(*currentBlock, FCmpInst::FCMP_OLE, val,
                            intrinsics->constantMinIntDouble, "");
        
        cont = createBasicBlock("D2I");

        BranchInst::Create(res, cont, test, currentBlock);
        node->addIncoming(intrinsics->constantMinInt, currentBlock);
        
        currentBlock = cont;
        llvm::Value* newVal = new FPToSIInst(val, Type::getInt32Ty(*llvmContext), "",
                                             currentBlock);
        BranchInst::Create(res, currentBlock);

        node->addIncoming(newVal, currentBlock);

        currentBlock = res;
        
        push(node, false);

        break;
      }

      case D2L : {
        pop(); // remove the 0 on the stack
        llvm::Value* val = pop();
        llvm::Value* test = new FCmpInst(*currentBlock, FCmpInst::FCMP_ONE,
                                         val, val, "");
        
        BasicBlock* res = createBasicBlock("D2L");
        PHINode* node = PHINode::Create(Type::getInt64Ty(*llvmContext), 4, "", res);
        node->addIncoming(intrinsics->constantLongZero, currentBlock);
        BasicBlock* cont = createBasicBlock("D2L");

        BranchInst::Create(res, cont, test, currentBlock);

        currentBlock = cont;
        
        test = new FCmpInst(*currentBlock, FCmpInst::FCMP_OGE, val,
                            intrinsics->constantMaxLongDouble, "");

        cont = createBasicBlock("D2L");

        BranchInst::Create(res, cont, test, currentBlock);
        node->addIncoming(intrinsics->constantMaxLong, currentBlock);

        currentBlock = cont;

        test = 
          new FCmpInst(*currentBlock, FCmpInst::FCMP_OLE, val,
                       intrinsics->constantMinLongDouble, "");
        
        cont = createBasicBlock("D2L");

        BranchInst::Create(res, cont, test, currentBlock);
        node->addIncoming(intrinsics->constantMinLong, currentBlock);
        
        currentBlock = cont;
        llvm::Value* newVal = new FPToSIInst(val, Type::getInt64Ty(*llvmContext), "",
                                             currentBlock);
        BranchInst::Create(res, currentBlock);

        node->addIncoming(newVal, currentBlock);

        currentBlock = res;
        
        push(node, false);
        push(intrinsics->constantZero, false);
        break;
      }

      case D2F :
        pop(); // remove the 0 on the stack
        push(new FPTruncInst(pop(), llvm::Type::getFloatTy(*llvmContext), "", currentBlock),
             false);
        break;

      case I2B : {
        Value* val = pop();
        if (val->getType() == Type::getInt32Ty(*llvmContext)) {
          val = new TruncInst(val, llvm::Type::getInt8Ty(*llvmContext), "", currentBlock);
        }
        push(new SExtInst(val, llvm::Type::getInt32Ty(*llvmContext), "", currentBlock),
             false);
        break;
      }

      case I2C : {
        Value* val = pop();
        if (val->getType() == Type::getInt32Ty(*llvmContext)) {
          val = new TruncInst(val, llvm::Type::getInt16Ty(*llvmContext), "", currentBlock);
        }
        push(new ZExtInst(val, llvm::Type::getInt32Ty(*llvmContext), "", currentBlock),
             false);
        break;
      }

      case I2S : {
        Value* val = pop();
        if (val->getType() == Type::getInt32Ty(*llvmContext)) {
          val = new TruncInst(val, llvm::Type::getInt16Ty(*llvmContext), "", currentBlock);
        }
        push(new SExtInst(val, llvm::Type::getInt32Ty(*llvmContext), "", currentBlock),
             false);
        break;
      }

      case LCMP : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();

        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val1,
                                         val2, "");
        
        BasicBlock* cont = createBasicBlock("LCMP");
        BasicBlock* res = createBasicBlock("LCMP");
        PHINode* node = PHINode::Create(Type::getInt32Ty(*llvmContext), 2, "", res);
        node->addIncoming(intrinsics->constantZero, currentBlock);
        
        BranchInst::Create(res, cont, test, currentBlock);
        currentBlock = cont;

        test = new ICmpInst(*currentBlock, ICmpInst::ICMP_SLT, val1, val2, "");
        node->addIncoming(intrinsics->constantMinusOne, currentBlock);

        cont = createBasicBlock("LCMP");
        BranchInst::Create(res, cont, test, currentBlock);
        currentBlock = cont;
        node->addIncoming(intrinsics->constantOne, currentBlock);
        BranchInst::Create(res, currentBlock);
        currentBlock = res;
        
        push(node, false);
        break;
      }

      case FCMPL : {
        llvm::Value* val2 = pop();
        llvm::Value* val1 = pop();
        compareFP(val1, val2, Type::getFloatTy(*llvmContext), false);
        break;
      }

      case FCMPG : {
        llvm::Value* val2 = pop();
        llvm::Value* val1 = pop();
        compareFP(val1, val2, Type::getFloatTy(*llvmContext), true);
        break;
      }

      case DCMPL : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        
        compareFP(val1, val2, Type::getDoubleTy(*llvmContext), false);
        break;
      }

      case DCMPG : {
        pop();
        llvm::Value* val2 = pop();
        pop();
        llvm::Value* val1 = pop();
        
        compareFP(val1, val2, Type::getDoubleTy(*llvmContext), false);
        break;
      }

      case IFEQ : {
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;

        Value* op = pop();
        Type* type = op->getType();
        Constant* val = Constant::getNullValue(type);
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, op,
                                         val, "");
        BasicBlock* ifFalse = createBasicBlock("false IFEQ");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IFNE : {
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        
        Value* op = pop();
        Type* type = op->getType();
        Constant* val = Constant::getNullValue(type);
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_NE, op,
                                         val, "");
        BasicBlock* ifFalse = createBasicBlock("false IFNE");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IFLT : {
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        Value* op = pop();
        Type* type = op->getType();
        Constant* val = Constant::getNullValue(type);
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_SLT, op,
                                         val, "");
        BasicBlock* ifFalse = createBasicBlock("false IFLT");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IFGE : {
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        Value* op = pop();
        Type* type = op->getType();
        Constant* val = Constant::getNullValue(type);
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_SGE, op,
                                         val, "");
        BasicBlock* ifFalse = createBasicBlock("false IFGE");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IFGT : {
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        Value* op = pop();
        Type* type = op->getType();
        Constant* val = Constant::getNullValue(type);
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_SGT, op,
                                         val, "");
        BasicBlock* ifFalse = createBasicBlock("false IFGT");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IFLE : {
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        Value* op = pop();
        Type* type = op->getType();
        Constant* val = Constant::getNullValue(type);
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_SLE, op,
                                         val, "");
        BasicBlock* ifFalse = createBasicBlock("false IFLE");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IF_ICMPEQ : {
        Value *val2 = popAsInt();
        Value *val1 = popAsInt();
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val1,
                                         val2, "");
        BasicBlock* ifFalse = createBasicBlock("false IF_ICMPEQ");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IF_ICMPNE : {
        Value *val2 = popAsInt();
        Value *val1 = popAsInt();
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_NE, val1,
                                         val2, "");
        BasicBlock* ifFalse = createBasicBlock("false IF_ICMPNE");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IF_ICMPLT : {
        Value *val2 = popAsInt();
        Value *val1 = popAsInt();
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_SLT,
                                         val1, val2, "");
        BasicBlock* ifFalse = createBasicBlock("false IF_IFCMPLT");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }
        
      case IF_ICMPGE : {
        Value *val2 = popAsInt();
        Value *val1 = popAsInt();
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_SGE,
                                         val1, val2, "");
        BasicBlock* ifFalse = createBasicBlock("false IF_ICMPGE");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IF_ICMPGT : {
        Value *val2 = popAsInt();
        Value *val1 = popAsInt();
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_SGT,
                                         val1, val2, "");
        BasicBlock* ifFalse = createBasicBlock("false IF_ICMPGT");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }
      
      case IF_ICMPLE : {
        Value *val2 = popAsInt();
        Value *val1 = popAsInt();
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_SLE,
                                         val1, val2, "");
        BasicBlock* ifFalse = createBasicBlock("false IF_ICMPLE");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IF_ACMPEQ : {
        Value *val2 = pop();
        Value *val1 = pop();
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ,
                                         val1, val2, "");
        BasicBlock* ifFalse = createBasicBlock("false IF_ACMPEQ");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case IF_ACMPNE : {
        Value *val2 = pop();
        Value *val1 = pop();
        uint32 tmp = i;
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_NE,
                                         val1, val2, "");
        BasicBlock* ifFalse = createBasicBlock("false IF_ACMPNE");
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }

      case GOTO : {
        uint32 tmp = i;
        branch(opcodeInfos[tmp + reader.readS2()],
               currentBlock);
        i += 2;
        break;
      }
      
      case JSR : {
        uint32 tmp = i;
        uint32 index = jsrIndex | 1;
        jsrIndex += 2;
        Value* expr = ConstantExpr::getIntToPtr(
                                    ConstantInt::get(Type::getInt64Ty(*llvmContext),
                                                     uint64_t (index)),
                                    intrinsics->JavaObjectType);
        push(expr, false);
        branch(opcodeInfos[tmp + reader.readS2()], currentBlock);
        i += 2;
        break;
      }

      case RET : {
        uint8 local = reader.readU1();
        i += 1;
        Value* _val = new LoadInst(
            objectLocals[local], "", false, currentBlock);
        Value* val = new PtrToIntInst(_val, Type::getInt32Ty(*llvmContext), "", currentBlock);
        SwitchInst* inst = SwitchInst::Create(val, jsrs[0], jsrs.size(),
                                          currentBlock);
        
        uint32 index = 0;
        for (std::vector<BasicBlock*>::iterator i = jsrs.begin(), 
            e = jsrs.end(); i!= e; ++i, index += 2) {
          inst->addCase(ConstantInt::get(Type::getInt32Ty(*llvmContext), index | 1), *i);
        }

        break;
      }

      case TABLESWITCH : {
        uint32 tmp = i;
        uint32 reste = (i + 1) & 3;
        uint32 filled = reste ?  (4 - reste) : 0;
        i += filled;
        reader.cursor += filled;
        Opinfo& def = opcodeInfos[tmp + reader.readU4()];
        i += 4;

        sint32 low = reader.readS4();
        i += 4;
        sint32 high = reader.readS4() + 1;
        i += 4;
        
        Value* index = pop(); 
        
        Type* type = index->getType();
        for (sint32 cur = low; cur < high; ++cur) {
          Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ,
                                    ConstantInt::get(type, cur), index, "");
          BasicBlock* falseBlock = createBasicBlock("continue tableswitch");
          Opinfo& info = opcodeInfos[tmp + reader.readU4()];
          i += 4;
          branch(cmp, info.newBlock, falseBlock, currentBlock, info);
          currentBlock = falseBlock;
        }
       
        
        branch(def, currentBlock);
        i = tmp + 12 + filled + ((high - low) << 2); 

        break;
      }

      case LOOKUPSWITCH : {
        uint32 tmp = i;
        uint32 filled = (3 - i) & 3;
        i += filled;
        reader.cursor += filled;
        Opinfo& def = opcodeInfos[tmp + reader.readU4()];
        i += 4;
        uint32 nbs = reader.readU4();
        i += 4;
        
        Value* key = pop();
        for (uint32 cur = 0; cur < nbs; ++cur) {
          Value* val = ConstantInt::get(Type::getInt32Ty(*llvmContext), reader.readU4());
          i += 4;
          Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val, key,
                                    "");
          BasicBlock* falseBlock = createBasicBlock("continue lookupswitch");
          Opinfo& info = opcodeInfos[tmp + reader.readU4()];
          i += 4;
          branch(cmp, info.newBlock, falseBlock, currentBlock, info);
          currentBlock = falseBlock;
        }
        branch(def, currentBlock);
        i = tmp + 8 + filled + (nbs << 3);
        break;
      }
      case IRETURN : {
        Value* val = pop();
        assert(val->getType()->isIntegerTy());
        convertValue(val, endNode->getType(), currentBlock, false);
        endNode->addIncoming(val, currentBlock);
        BranchInst::Create(endBlock, currentBlock);
        break;
      }
      case LRETURN :
        pop(); // remove the 0 on the stack
        endNode->addIncoming(pop(), currentBlock);
        BranchInst::Create(endBlock, currentBlock);
        break;

      case FRETURN :
        endNode->addIncoming(pop(), currentBlock);
        BranchInst::Create(endBlock, currentBlock);
        break;

      case DRETURN :
        pop(); // remove the 0 on the stack
        endNode->addIncoming(pop(), currentBlock);
        BranchInst::Create(endBlock, currentBlock);
        break;
      
      case ARETURN :
        endNode->addIncoming(pop(), currentBlock);
        BranchInst::Create(endBlock, currentBlock);
        break;
      
      case RETURN : {
        // Prevent a javac bug.
        if (endNode != 0) {
          endNode->addIncoming(Constant::getNullValue(endNode->getType()),
                               currentBlock);
        }
        BranchInst::Create(endBlock, currentBlock);
        break;
      }

      case GETSTATIC : {
        uint16 index = reader.readU2();
        i += 2;
        getStaticField(index);
        break;
      }

      case PUTSTATIC : {
        uint16 index = reader.readU2();
        i += 2;
        setStaticField(index);
        break;
      }

      case GETFIELD : {
        uint16 index = reader.readU2();
        i += 2;
        getVirtualField(index);
        break;
      }

      case PUTFIELD : {
        uint16 index = reader.readU2();
        i += 2;
        setVirtualField(index);
        break;
      }

      case INVOKEVIRTUAL : {
        uint16 index = reader.readU2();
        i += 2;
        invokeVirtual(index);
        break;
      }

      case INVOKESPECIAL : {
        uint16 index = reader.readU2();
        i += 2;
        invokeSpecial(index);
        break;
      }

      case INVOKESTATIC : {
        uint16 index = reader.readU2();
        i += 2;
        invokeStatic(index);
        break;
      }

      case INVOKEINTERFACE : {
        uint16 index = reader.readU2();
        i += 2;
        invokeInterface(index);
        i += 2;
        break;
      }

      case NEW : {
        uint16 index = reader.readU2();
        i += 2;
        invokeNew(index);
        break;
      }

      case NEWARRAY :
      case ANEWARRAY : {
        
        Constant* sizeElement = 0;
        Value* TheVT = 0;
        Value* valCl = 0;
        UserClassArray* dcl = 0;

        if (bytecode == NEWARRAY) {
          uint8 id = reader.readU1();
          i += 1;
          uint8 charId = arrayType(compilingMethod, id);
          JnjvmBootstrapLoader* loader = 
            compilingClass->classLoader->bootstrapLoader;
          dcl = loader->getArrayClass(id);
          valCl = TheCompiler->getNativeClass(dcl);

          LLVMAssessorInfo& LAI = TheCompiler->AssessorInfo[charId];
          sizeElement = ConstantInt::get(Type::getInt32Ty(*llvmContext),
                                                    LAI.logSizeInBytesConstant);
          if (TheCompiler->isStaticCompiling() &&
              valCl->getType() != intrinsics->JavaClassArrayType) {
            valCl = new LoadInst(valCl, "", currentBlock);
            TheVT = CallInst::Create(intrinsics->GetVTFromClassArrayFunction,
                                     valCl, "", currentBlock);
          } else {
            TheVT = TheCompiler->getVirtualTable(dcl->virtualVT);
          }
        } else {
          uint16 index = reader.readU2();
          i += 2;
          CommonClass* cl =
            compilingClass->ctpInfo->getMethodClassIfLoaded(index); 

          if (cl && (!cl->isClass() || cl->asClass()->isResolved())) {
            JnjvmClassLoader* JCL = cl->classLoader;
            const UTF8* arrayName = JCL->constructArrayName(1, cl->name);
          
            dcl = JCL->constructArray(arrayName);
            valCl = TheCompiler->getNativeClass(dcl);
            
            // If we're static compiling and the class is not a class we
            // are compiling, the result of getNativeClass is a pointer to
            // the class. Load it.
            if (TheCompiler->isStaticCompiling() && 
                valCl->getType() != intrinsics->JavaClassArrayType) {
              valCl = new LoadInst(valCl, "", currentBlock);
              TheVT = CallInst::Create(intrinsics->GetVTFromClassArrayFunction,
                                       valCl, "", currentBlock);
            } else {
              TheVT = TheCompiler->getVirtualTable(dcl->virtualVT);
            }

          } else {
            Type* Ty = 
              PointerType::getUnqual(intrinsics->VTType);
            Value* args[3]= { TheCompiler->getNativeClass(compilingClass),
                              ConstantInt::get(Type::getInt32Ty(*llvmContext), index),
                              Constant::getNullValue(Ty) };
            TheVT = CallInst::Create(intrinsics->GetArrayClassFunction, args,
                                     "", currentBlock);
          }

          sizeElement = intrinsics->constantPtrLogSize;
        }
        Value* arg1 = popAsInt();

        if (TheCompiler->hasExceptionsEnabled()) {
          Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_SLT, arg1,
                                    intrinsics->constantZero, "");

          BasicBlock* BB1 = createBasicBlock("");
          BasicBlock* BB2 = createBasicBlock("");

          BranchInst::Create(BB1, BB2, cmp, currentBlock);
          currentBlock = BB1;
          throwRuntimeException(intrinsics->NegativeArraySizeExceptionFunction, arg1);
          currentBlock = BB2;
        
          cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_SGT, arg1,
                             intrinsics->MaxArraySizeConstant, "");

          BB1 = createBasicBlock("");
          BB2 = createBasicBlock("");

          BranchInst::Create(BB1, BB2, cmp, currentBlock);
          currentBlock = BB1;
          throwRuntimeException(intrinsics->OutOfMemoryErrorFunction, arg1);
          currentBlock = BB2;
        }
        
        Value* mult = BinaryOperator::CreateShl(arg1, sizeElement, "",
                                                currentBlock);
        Value* size =
          BinaryOperator::CreateAdd(intrinsics->JavaArraySizeConstant, mult,
                                    "", currentBlock);
        TheVT = new BitCastInst(TheVT, intrinsics->ptrType, "", currentBlock);
        Instruction* res = invoke(intrinsics->VTAllocateFunction, size, TheVT, "",
                                  currentBlock);
        Value* cast = new BitCastInst(res, intrinsics->JavaArrayType, "",
                                      currentBlock);

        // Set the size
        Value* gep4[2] = { intrinsics->constantZero,
                           intrinsics->JavaArraySizeOffsetConstant };
        Value* GEP = GetElementPtrInst::Create(cast, gep4, "", currentBlock);

        arg1 = new IntToPtrInst(arg1, intrinsics->ptrType, "", currentBlock);
        new StoreInst(arg1, GEP, currentBlock);
       
        addHighLevelType(res, dcl ? dcl : upcalls->ArrayOfObject);
        res = new BitCastInst(res, intrinsics->JavaObjectType, "", currentBlock);
        push(res, false, dcl ? dcl : upcalls->ArrayOfObject);

        break;
      }

      case ARRAYLENGTH : {
        Value* val = pop();
        JITVerifyNull(val);
        push(arraySize(val), false);
        break;
      }

      case ATHROW : {
        llvm::Value* arg = pop();
        throwException(arg);
        break;
      }

      case CHECKCAST :
        if (!TheCompiler->hasExceptionsEnabled()) {
          i += 2;
          reader.cursor += 2;
          break;
        }

      case INSTANCEOF : {
        
        bool checkcast = (bytecode == CHECKCAST);
        
        BasicBlock* exceptionCheckcast = 0;
        BasicBlock* endCheckcast = 0;

        uint16 index = reader.readU2();
        i += 2;
        UserCommonClass* cl = 0;
        Value* clVar = getResolvedCommonClass(index, true, &cl);
        Value* obj = top();
        Value* args[2] = { obj, clVar };
        Value* cmp = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, obj,
                                  intrinsics->JavaObjectNullConstant, "");
        BasicBlock* endBlock = createBasicBlock("end type compare");
        PHINode* node = PHINode::Create(Type::getInt1Ty(*llvmContext), 2, "", endBlock);
        
        if (checkcast) {
          exceptionCheckcast = createBasicBlock("false checkcast");

        
          endCheckcast = createBasicBlock("null checkcast");
          BasicBlock* ifFalse = createBasicBlock("non null checkcast");

          BranchInst::Create(endCheckcast, ifFalse, cmp, currentBlock);
          currentBlock = exceptionCheckcast;
          throwRuntimeException(intrinsics->ClassCastExceptionFunction, args, 2);
          currentBlock = ifFalse;
        } else {
          BasicBlock* ifFalse = createBasicBlock("false type compare");
          BranchInst::Create(endBlock, ifFalse, cmp, currentBlock);
          node->addIncoming(ConstantInt::getFalse(*llvmContext), currentBlock);
          currentBlock = ifFalse;
        }

        Value* TheVT = 0;
        if (!cl || TheCompiler->isStaticCompiling()) {
          TheVT = CallInst::Create(intrinsics->GetVTFromCommonClassFunction,
                                   clVar, "", currentBlock);
        } else {
          TheVT = TheCompiler->getVirtualTable(cl->virtualVT);
        }

        
        Value* objVT = CallInst::Create(intrinsics->GetVTFunction, obj, "",
                                       currentBlock);
        Value* classArgs[2] = { objVT, TheVT };
         
        Value* res = 0;
        if (cl) {
          if (cl->isSecondaryClass()) {
        	  if (TheCompiler->isStaticCompiling())
        		  res = CallInst::Create(intrinsics->IsSecondaryClassFunction,
                                   classArgs, "", currentBlock);
        	  else
        		  res = CallInst::Create(intrinsics->IsSecondaryClassFunctionInner,
        		                                     classArgs, "", currentBlock);
          } else {
            Value* inDisplay = CallInst::Create(intrinsics->GetDisplayFunction,
                                                objVT, "", currentBlock);
            
            uint32 depth = cl->virtualVT->depth;
            ConstantInt* CI = ConstantInt::get(Type::getInt32Ty(*llvmContext), depth);
            Value* displayArgs[2] = { inDisplay, CI };
            Value* VTInDisplay = 
              CallInst::Create(intrinsics->GetVTInDisplayFunction,
                               displayArgs, "", currentBlock);
             
            res = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, VTInDisplay,
                               TheVT, "");
          }
        } else {
        	if (TheCompiler->isStaticCompiling())
        		res = CallInst::Create(intrinsics->IsSubclassOfFunction,
                                 classArgs, "", currentBlock);
        	else
        		res = CallInst::Create(intrinsics->IsSubclassOfFunctionInner,
        		                                 classArgs, "", currentBlock);
        }

        node->addIncoming(res, currentBlock);
        BranchInst::Create(endBlock, currentBlock);
        currentBlock = endBlock;

        if (checkcast) {
          BranchInst::Create(endCheckcast, exceptionCheckcast, node,
                             currentBlock);
          currentBlock = endCheckcast;
        } else {
          pop();
          push(new ZExtInst(node, Type::getInt32Ty(*llvmContext), "", currentBlock),
               false);
        }

        break;
      }

      case MONITORENTER : {
        bool thisReference = isThisReference(currentStackIndex - 1);
        Value* obj = pop();
        if (!thisReference) JITVerifyNull(obj);
        monitorEnter(obj);
        break;
      }

      case MONITOREXIT : {
        bool thisReference = isThisReference(currentStackIndex - 1);
        Value* obj = pop();
        if (!thisReference) JITVerifyNull(obj);
        monitorExit(obj);
        break;
      }

      case MULTIANEWARRAY : {
        uint16 index = reader.readU2();
        i += 2;
        uint8 dim = reader.readU1();
        i += 1;
        
        UserCommonClass* dcl = 0; 
        Value* valCl = getResolvedCommonClass(index, true, &dcl);
        Value** args = (Value**)allocator.Allocate(sizeof(Value*) * (dim + 2));
        args[0] = valCl;
        args[1] = ConstantInt::get(Type::getInt32Ty(*llvmContext), dim);

        for (int cur = dim + 1; cur >= 2; --cur)
          args[cur] = pop();
        
        std::vector<Value*> Args;
        for (sint32 v = 0; v < dim + 2; ++v) {
          Args.push_back(args[v]);
        }
        push(invoke(intrinsics->MultiCallNewFunction, Args, "", currentBlock),
             false, dcl ? dcl : upcalls->ArrayOfObject);
        break;
      }

      case WIDE :
        wide = true;
        break;

      case IFNULL : {
        uint32 tmp = i;
        llvm::Value* val = pop();
        Constant* nil = Constant::getNullValue(val->getType());
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_EQ, val,
                                         nil, "");
        BasicBlock* ifFalse = createBasicBlock("true IFNULL");
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }
      
      case IFNONNULL : {
        uint32 tmp = i;
        llvm::Value* val = pop();
        Constant* nil = Constant::getNullValue(val->getType());
        llvm::Value* test = new ICmpInst(*currentBlock, ICmpInst::ICMP_NE, val,
                                         nil, "");
        BasicBlock* ifFalse = createBasicBlock("false IFNONNULL");
        Opinfo& ifTrueInfo = opcodeInfos[tmp + reader.readS2()];
        i += 2;
        BasicBlock* ifTrue = ifTrueInfo.newBlock;
        branch(test, ifTrue, ifFalse, currentBlock, ifTrueInfo);
        currentBlock = ifFalse;
        break;
      }
      
      default : {
        fprintf(stderr, "I haven't verified your class file and it's malformed:"
                    " unknown bytecode %d in %s.%s\n!", bytecode,
                    UTF8Buffer(compilingClass->name).cString(),
                    UTF8Buffer(compilingMethod->name).cString());
        abort();
      }
    } 
  }
}

void JavaJIT::exploreOpcodes(Reader& reader, uint32 codeLength) {
	PRINT_DEBUG(JNJVM_COMPILE, 1, DARK_YELLOW, "exploreOpcodes %d \n", codeLength);

  bool wide = false;
  uint32 start = reader.cursor;
  for(uint32 i = 0; i < codeLength; ++i) {
    reader.cursor = start + i;
    uint8 bytecode = reader.readU1();

    PRINT_DEBUG(JNJVM_COMPILE, 1, DARK_CYAN, "\t[at %5d] %-5d ", i,
                bytecode);
    PRINT_DEBUG(JNJVM_COMPILE, 1, LIGHT_BLUE, "exploring ");
    PRINT_DEBUG(JNJVM_COMPILE, 1, LIGHT_CYAN, OpcodeNames[bytecode]);
    PRINT_DEBUG(JNJVM_COMPILE, 1, LIGHT_BLUE, "\n");
    
    switch (bytecode) {
     
      case NOP :
      case ACONST_NULL : 
      case ICONST_M1 :
      case ICONST_0 :
      case ICONST_1 :
      case ICONST_2 :
      case ICONST_3 :
      case ICONST_4 :
      case ICONST_5 :
      case LCONST_0 :
      case LCONST_1 :
      case FCONST_0 :
      case FCONST_1 : 
      case FCONST_2 :
      case DCONST_0 :
      case DCONST_1 : break;

      case BIPUSH : ++i; break;
      
      case SIPUSH : i += 2; break;
      
      case LDC : ++i; break;

      case LDC_W : 
      case LDC2_W : i += 2; break;

      case ILOAD :
      case LLOAD :
      case FLOAD :
      case DLOAD :
      case ALOAD :
        i += WCALC(1, wide);
        break;
      
      case ILOAD_0 :
      case ILOAD_1 :
      case ILOAD_2 :
      case ILOAD_3 :
      case LLOAD_0 :
      case LLOAD_1 :
      case LLOAD_2 :
      case LLOAD_3 :
      case FLOAD_0 :
      case FLOAD_1 :
      case FLOAD_2 :
      case FLOAD_3 :
      case DLOAD_0 :
      case DLOAD_1 :
      case DLOAD_2 :
      case DLOAD_3 :
      case ALOAD_0 :
      case ALOAD_1 :
      case ALOAD_2 :
      case ALOAD_3 :
      case IALOAD :
      case LALOAD :
      case FALOAD :
      case DALOAD :
      case AALOAD :
      case BALOAD :
      case CALOAD :
      case SALOAD : break;

      case ISTORE :
      case LSTORE :
      case FSTORE :
      case DSTORE :
      case ASTORE :
        i += WCALC(1, wide);
        break;
      
      case ISTORE_0 :
      case ISTORE_1 :
      case ISTORE_2 :
      case ISTORE_3 :
      case LSTORE_0 :
      case LSTORE_1 :
      case LSTORE_2 :
      case LSTORE_3 :
      case FSTORE_0 :
      case FSTORE_1 :
      case FSTORE_2 :
      case FSTORE_3 :
      case DSTORE_0 :
      case DSTORE_1 :
      case DSTORE_2 :
      case DSTORE_3 :
      case ASTORE_1 :
      case ASTORE_2 :
      case ASTORE_3 :
      case IASTORE :
      case LASTORE :
      case FASTORE :
      case DASTORE :
      case AASTORE :
      case BASTORE :
      case CASTORE :
      case SASTORE :
      case POP :
      case POP2 :
      case DUP :
      case DUP_X1 :
      case DUP_X2 :
      case DUP2 :
      case DUP2_X1 :
      case DUP2_X2 :
      case SWAP :
      case IADD :
      case LADD :
      case FADD :
      case DADD :
      case ISUB :
      case LSUB :
      case FSUB :
      case DSUB :
      case IMUL :
      case LMUL :
      case FMUL :
      case DMUL :
      case IDIV :
      case LDIV :
      case FDIV :
      case DDIV :
      case IREM :
      case LREM :
      case FREM :
      case DREM :
      case INEG :
      case LNEG :
      case FNEG :
      case DNEG :
      case ISHL :
      case LSHL :
      case ISHR :
      case LSHR :
      case IUSHR :
      case LUSHR :
      case IAND :
      case LAND :
      case IOR :
      case LOR :
      case IXOR :
      case LXOR : break;
      
      case ASTORE_0 :
        if (!isStatic(compilingMethod->access)) overridesThis = true;
        break;

      case IINC :
        i += WCALC(2, wide);
        break;
      
      case I2L :
      case I2F :
      case I2D :
      case L2I :
      case L2F :
      case L2D :
      case F2I :
      case F2L :
      case F2D :
      case D2I :
      case D2L :
      case D2F :
      case I2B :
      case I2C :
      case I2S :
      case LCMP :
      case FCMPL :
      case FCMPG :
      case DCMPL :
      case DCMPG : break;

      case IFEQ :
      case IFNE :
      case IFLT :
      case IFGE :
      case IFGT :
      case IFLE :
      case IF_ICMPEQ :
      case IF_ICMPNE :
      case IF_ICMPLT :
      case IF_ICMPGE :
      case IF_ICMPGT :
      case IF_ICMPLE :
      case IF_ACMPEQ :
      case IF_ACMPNE :
      case GOTO :
      case IFNULL :
      case IFNONNULL : {
        uint32 tmp = i;
        uint16 index = tmp + reader.readU2();
        if (index < i) opcodeInfos[index].backEdge = true;
        i += 2;
        if (!(opcodeInfos[index].newBlock))
          opcodeInfos[index].newBlock = createBasicBlock("");
        break;
      }
      
      case JSR : {
        uint32 tmp = i;
        uint16 index = tmp + reader.readU2();
        i += 2;
        if (!(opcodeInfos[index].newBlock)) {
          BasicBlock* block = createBasicBlock("JSR");
          opcodeInfos[index].newBlock = block;
        }
        if (!(opcodeInfos[tmp + 3].newBlock)) {
          BasicBlock* block = createBasicBlock("JSR2");
          jsrs.push_back(block);
          opcodeInfos[tmp + 3].newBlock = block;
        } else {
          jsrs.push_back(opcodeInfos[tmp + 3].newBlock);
        }
        break;
      }

      case RET : ++i; break;

      case TABLESWITCH : {
        uint32 tmp = i;
        uint32 reste = (i + 1) & 3;
        uint32 filled = reste ? (4 - reste) : 0; 
        i += filled;
        reader.cursor += filled;
        uint32 index = tmp + reader.readU4();
        i += 4;
        if (!(opcodeInfos[index].newBlock)) {
          BasicBlock* block = createBasicBlock("tableswitch");
          opcodeInfos[index].newBlock = block;
        }
        uint32 low = reader.readU4();
        i += 4;
        uint32 high = reader.readU4() + 1;
        i += 4;
        uint32 depl = high - low;
        for (uint32 cur = 0; cur < depl; ++cur) {
          uint32 index2 = tmp + reader.readU4();
          i += 4;
          if (!(opcodeInfos[index2].newBlock)) {
            BasicBlock* block = createBasicBlock("tableswitch");
            opcodeInfos[index2].newBlock = block;
          }
        }
        i = tmp + 12 + filled + (depl << 2);
        break;
      }

      case LOOKUPSWITCH : {
        uint32 tmp = i;
        uint32 filled = (3 - i) & 3;
        i += filled;
        reader.cursor += filled;
        uint32 index = tmp + reader.readU4();
        i += 4;
        if (!(opcodeInfos[index].newBlock)) {
          BasicBlock* block = createBasicBlock("tableswitch");
          opcodeInfos[index].newBlock = block;
        }
        uint32 nbs = reader.readU4();
        i += 4;
        for (uint32 cur = 0; cur < nbs; ++cur) {
          i += 4;
          reader.cursor += 4;
          uint32 index2 = tmp + reader.readU4();
          i += 4;
          if (!(opcodeInfos[index2].newBlock)) {
            BasicBlock* block = createBasicBlock("tableswitch");
            opcodeInfos[index2].newBlock = block;
          }
        }
        
        i = tmp + 8 + filled + (nbs << 3);
        break;
      }

      case IRETURN :
      case LRETURN :
      case FRETURN :
      case DRETURN :
      case ARETURN :
      case RETURN : break;
      
      case GETSTATIC :
      case PUTSTATIC :
      case GETFIELD :
      case PUTFIELD :
      case INVOKEVIRTUAL :
      case INVOKESPECIAL :
      case INVOKESTATIC :
        i += 2;
        break;
      
      case INVOKEINTERFACE :
        i += 4;
        break;

      case NEW :
        i += 2;
        break;

      case NEWARRAY :
        ++i;
        break;
      
      case ANEWARRAY :
        i += 2;
        break;

      case ARRAYLENGTH :
      case ATHROW : break;

      case CHECKCAST :
        i += 2;
        break;

      case INSTANCEOF :
        i += 2;
        break;
      
      case MONITORENTER :
        break;

      case MONITOREXIT :
        break;
      
      case MULTIANEWARRAY :
        i += 3;
        break;

      case WIDE :
        wide = true;
        break;

      default : {
        fprintf(stderr, "I haven't verified your class file and it's malformed:"
                    " unknown bytecode %d in %s.%s!\n", bytecode,
                    UTF8Buffer(compilingClass->name).cString(),
                    UTF8Buffer(compilingMethod->name).cString());
        abort();
      }
    }
  }
}

bool JavaJIT::canInlineLoadConstant(uint16 index) {
  JavaConstantPool* ctpInfo = compilingClass->ctpInfo;
  uint8 type = ctpInfo->typeAt(index);
  if (type == JavaConstantPool::ConstantString
      || type == JavaConstantPool::ConstantClass) {
    return false;
  } else {
    return true;
  }
}

static uint8 getReceiver(const std::vector<uint8>& stack, Signdef* signature) {
  uint32_t index = stack.size() - 1;
  Typedef* const* arguments = signature->getArgumentsType();
  for (uint32 i = 0; i < signature->nbArguments; i++) {
    index--;
    if (arguments[i]->isDouble() || arguments[i]->isLong()) {
      index--;
    }
  }
  return stack[index];
}

static void updateStack(std::vector<uint8>& stack, Signdef* signature, uint8 bytecode) {
  Typedef* const* arguments = signature->getArgumentsType();
  if (bytecode != INVOKESTATIC) {
    stack.pop_back();
  }
  for (uint32 i = 0; i < signature->nbArguments; i++) {
    stack.pop_back();
    if (arguments[i]->isDouble() || arguments[i]->isLong()) {
      stack.pop_back();
    }
  }
  if (!signature->getReturnType()->isVoid()) {
    stack.push_back(bytecode);
    if (signature->getReturnType()->isLong()
        || signature->getReturnType()->isDouble()) {
      stack.push_back(bytecode);
    }
  }
}

bool JavaJIT::analyzeForInlining(Reader& reader, uint32 codeLength) {
  JavaConstantPool* ctpInfo = compilingClass->ctpInfo;
  bool wide = false;
  uint32 start = reader.cursor;
  std::vector<uint8_t> stack;
  for(uint32 i = 0; i < codeLength; ++i) {
    reader.cursor = start + i;
    uint8 bytecode = reader.readU1();
    
    switch (bytecode) { 
      case NOP :
        break;

      case ACONST_NULL :
      case ICONST_M1 :
      case ICONST_0 :
      case ICONST_1 :
      case ICONST_2 :
      case ICONST_3 :
      case ICONST_4 :
      case ICONST_5 :
      case FCONST_0 :
      case FCONST_1 : 
      case FCONST_2 :
        stack.push_back(bytecode);
        break;

      case LCONST_0 :
      case LCONST_1 :
      case DCONST_0 :
      case DCONST_1 :
        stack.push_back(bytecode);
        stack.push_back(bytecode);
        break;

      case BIPUSH :
        ++i;
        stack.push_back(bytecode);
        break;
      
      case SIPUSH :
        i += 2;
        stack.push_back(bytecode);
        break;
      
      case LDC :
        i++;
        stack.push_back(bytecode);
        if (!canInlineLoadConstant(reader.readU1())) return false;
        break;

      case LDC_W :
        i += 2;
        stack.push_back(bytecode);
        if (!canInlineLoadConstant(reader.readS2())) return false;
        break;
      

      case LDC2_W :
        i += 2;
        stack.push_back(bytecode);
        stack.push_back(bytecode);
        if (!canInlineLoadConstant(reader.readS2())) return false;
        break;
      
      case ILOAD :
      case FLOAD :
      case ALOAD :
        i += WCALC(1, wide);
        stack.push_back(bytecode);
        break;

      case LLOAD :
      case DLOAD :
        i += WCALC(1, wide);
        stack.push_back(bytecode);
        stack.push_back(bytecode);
        break;
      
      case ILOAD_0 :
      case ILOAD_1 :
      case ILOAD_2 :
      case ILOAD_3 :
      case FLOAD_0 :
      case FLOAD_1 :
      case FLOAD_2 :
      case FLOAD_3 :
      case ALOAD_0 :
      case ALOAD_1 :
      case ALOAD_2 :
      case ALOAD_3 :
        stack.push_back(bytecode);
        break;

      case LLOAD_0 :
      case LLOAD_1 :
      case LLOAD_2 :
      case LLOAD_3 :
      case DLOAD_0 :
      case DLOAD_1 :
      case DLOAD_2 :
      case DLOAD_3 :
        stack.push_back(bytecode);
        stack.push_back(bytecode);
        break;

      case IALOAD :
      case FALOAD :
      case AALOAD :
      case BALOAD :
      case CALOAD :
      case SALOAD :
        stack.pop_back();
        stack.pop_back();
        stack.push_back(bytecode);
        return false;

      case LALOAD :
      case DALOAD :
        stack.pop_back();
        stack.pop_back();
        stack.push_back(bytecode);
        stack.push_back(bytecode);
        return false;

      case ISTORE :
      case FSTORE :
      case ASTORE :
        stack.pop_back();
        i += WCALC(1, wide);
        break;

      case LSTORE :
      case DSTORE :
        stack.pop_back();
        stack.pop_back();
        i += WCALC(1, wide);
        break;
      
      case ISTORE_0 :
      case ISTORE_1 :
      case ISTORE_2 :
      case ISTORE_3 :
      case FSTORE_0 :
      case FSTORE_1 :
      case FSTORE_2 :
      case FSTORE_3 :
      case ASTORE_1 :
      case ASTORE_2 :
      case ASTORE_3 :
        stack.pop_back();
        break;

      case ASTORE_0 :
        stack.pop_back();
        if (!isStatic(compilingMethod->access)) return false;
        break;

      case DSTORE_0 :
      case DSTORE_1 :
      case DSTORE_2 :
      case DSTORE_3 :
      case LSTORE_0 :
      case LSTORE_1 :
      case LSTORE_2 :
      case LSTORE_3 :
        stack.pop_back();
        stack.pop_back();
        break;

      case IASTORE :
      case FASTORE :
      case AASTORE :
      case BASTORE :
      case CASTORE :
      case SASTORE :
        stack.pop_back();
        stack.pop_back();
        stack.pop_back();
        return false;

      case LASTORE :
      case DASTORE :
        stack.pop_back();
        stack.pop_back();
        stack.pop_back();
        stack.pop_back();
        return false;

      case POP :
        stack.pop_back();
        break;

      case POP2 :
        stack.pop_back();
        stack.pop_back();
        break;

      case DUP :
        stack.push_back(stack.back());
        break;

      case DUP_X1 : {
        uint8 one = stack.back(); stack.pop_back();
        uint8 two = stack.back(); stack.pop_back();
        
        stack.push_back(one);
        stack.push_back(two);
        stack.push_back(one);
        break;
      }

      case DUP_X2 : {
        uint8 one = stack.back(); stack.pop_back();
        uint8 two = stack.back(); stack.pop_back();
        uint8 three = stack.back(); stack.pop_back();

        stack.push_back(one);
        stack.push_back(three);
        stack.push_back(two);
        stack.push_back(one);
        break;
      }

      case DUP2 : {
        uint8 one = stack.back(); stack.pop_back();
        uint8 two = stack.back(); stack.pop_back();
        
        stack.push_back(two);
        stack.push_back(one);
        stack.push_back(two);
        stack.push_back(one);
        break;
      }

      case DUP2_X1 : {
        uint8 one = stack.back(); stack.pop_back();
        uint8 two = stack.back(); stack.pop_back();
        uint8 three = stack.back(); stack.pop_back();

        stack.push_back(two);
        stack.push_back(one);
        stack.push_back(three);
        stack.push_back(two);
        stack.push_back(one);

        break;
      }

      case DUP2_X2 : {
        uint8 one = stack.back(); stack.pop_back();
        uint8 two = stack.back(); stack.pop_back();
        uint8 three = stack.back(); stack.pop_back();
        uint8 four = stack.back(); stack.pop_back();

        stack.push_back(two);
        stack.push_back(one);
        stack.push_back(four);
        stack.push_back(three);
        stack.push_back(two);
        stack.push_back(one);

        break;
      }

      case SWAP : {
        uint8 one = stack.back(); stack.pop_back();
        uint8 two = stack.back(); stack.pop_back();
        stack.push_back(one);
        stack.push_back(two);
        break;
      }

      case IADD :
      case FADD :
      case ISUB :
      case FSUB :
      case IMUL :
      case FMUL :
      case FDIV :
      case FREM :
      case ISHL :
      case ISHR :
      case IUSHR :
      case IAND :
      case IOR :
      case IXOR :
        stack.pop_back();
        stack.pop_back();
        stack.push_back(bytecode);
        break;

      case LADD :
      case DADD :
      case DDIV :
      case LMUL :
      case DMUL :
      case DREM :
      case LSUB :
      case DSUB :
        stack.pop_back();
        stack.pop_back();
        stack.push_back(bytecode);
        stack.push_back(bytecode);
        break;

      case IREM :
      case IDIV :
      case LREM :
      case LDIV :
        return false;

      case INEG :
      case FNEG :
        stack.pop_back();
        stack.push_back(bytecode);
        break;

      case LNEG :
      case DNEG :
        stack.pop_back();
        stack.pop_back();
        stack.push_back(bytecode);
        stack.push_back(bytecode);
        break;

      case LSHL :
      case LSHR :
      case LUSHR :
      case LAND :
      case LOR :
      case LXOR :
        stack.pop_back();
        stack.pop_back();
        stack.pop_back();
        stack.push_back(bytecode);
        stack.push_back(bytecode);
        break;

      case IINC :
        i += WCALC(2, wide);
        break;
      
      case I2L :
      case I2D :
      case F2L :
      case F2D :
        stack.pop_back();
        stack.push_back(bytecode);
        stack.push_back(bytecode);
        break;

      case I2F :
      case F2I :
      case I2B :
      case I2C :
      case I2S :
        stack.pop_back();
        stack.push_back(bytecode);
        break;

      case L2I :
      case L2F :
      case D2I :
      case D2F :
        stack.pop_back();
        stack.pop_back();
        stack.push_back(bytecode);
        break;

      case L2D :
      case D2L :
        stack.pop_back();
        stack.pop_back();
        stack.push_back(bytecode);
        stack.push_back(bytecode);
        break;

      case FCMPL :
      case FCMPG :
        stack.pop_back();
        stack.pop_back();
        stack.push_back(bytecode);
        break;

      case LCMP :
      case DCMPL :
      case DCMPG :
        stack.pop_back();
        stack.pop_back();
        stack.pop_back();
        stack.pop_back();
        stack.push_back(bytecode);
        break;

      case IFEQ :
      case IFNE :
      case IFLT :
      case IFGE :
      case IFGT :
      case IFLE : {
        i += 2;
        stack.pop_back();
        if (stack.size() > 0) return false;
        break;
      }

      case IF_ICMPEQ :
      case IF_ICMPNE :
      case IF_ICMPLT :
      case IF_ICMPGE :
      case IF_ICMPGT :
      case IF_ICMPLE :
      case IF_ACMPEQ :
      case IF_ACMPNE : {
        i += 2;
        stack.pop_back();
        stack.pop_back();
        if (stack.size() > 0) return false;
        break;
      }
      case GOTO : {
        i += 2;
        if (stack.size() > 0) return false;
        break;
      }
      
      case JSR : {
        i += 2;
        return false;
      }

      case RET : {
        ++i;
        return false;
      }

      case TABLESWITCH : {
        stack.pop_back();
        if (stack.size() > 0) return false;
        uint32 tmp = i;
        uint32 reste = (i + 1) & 3;
        uint32 filled = reste ?  (4 - reste) : 0;
        i += filled;
        reader.cursor += filled;
        reader.readU4();
        i += 4;

        sint32 low = reader.readS4();
        i += 4;
        sint32 high = reader.readS4() + 1;
        i += 4;
        for (sint32 cur = low; cur < high; ++cur) {
          reader.readU4();
          i += 4;
        }
       
        i = tmp + 12 + filled + ((high - low) << 2); 
        break;
      }

      case LOOKUPSWITCH : {
        stack.pop_back();
        if (stack.size() > 0) return false;
        uint32 tmp = i;
        uint32 filled = (3 - i) & 3;
        i += filled;
        reader.cursor += filled;
        reader.readU4();
        i += 4;
        uint32 nbs = reader.readU4();
        i += 4;
        for (uint32 cur = 0; cur < nbs; ++cur) {
          i += 4;
          reader.cursor += 4;
          reader.readU4();
          i += 4;
        }
        
        i = tmp + 8 + filled + (nbs << 3);
        break;
      }

      case IRETURN :
      case FRETURN :
      case ARETURN :
        stack.pop_back();
        break;

      case RETURN :
        break;
      
      case LRETURN :
      case DRETURN :
        stack.pop_back();
        stack.pop_back();
        break;
      
      case GETSTATIC :
      case PUTSTATIC : {
        uint16 index = reader.readU2();
        Typedef* sign = ctpInfo->infoOfField(index);
        JavaField* field = ctpInfo->lookupField(index, true);
        if (field == NULL) return false;
        if (needsInitialisationCheck(field->classDef)) return false;
        if (bytecode == GETSTATIC) {
          stack.push_back(bytecode);
          if (sign->isDouble() || sign->isLong()) {
            stack.push_back(bytecode);
          }
        } else {
          stack.pop_back();
          if (sign->isDouble() || sign->isLong()) {
            stack.pop_back();
          }
        }
        i += 2;
        break;
      }

      case PUTFIELD : {
        if (isStatic(compilingMethod->access)) return false;
        i += 2;
        stack.pop_back(); // value
        uint16 index = reader.readU2();
        Typedef* sign = ctpInfo->infoOfField(index);
        if (sign->isDouble() || sign->isLong()) {
          stack.pop_back(); // value
        }
        if (stack.back() != ALOAD_0) return false;
        stack.pop_back(); // object
        break;
      }

      case GETFIELD : {
        if (isStatic(compilingMethod->access)) return false;
        if (stack.back() != ALOAD_0) return false;
        i += 2;
        stack.pop_back(); // object
        uint16 index = reader.readU2();
        Typedef* sign = ctpInfo->infoOfField(index);
        stack.push_back(bytecode);
        if (sign->isDouble() || sign->isLong()) {
          stack.push_back(bytecode);
        }
        break;
      }

      case INVOKEVIRTUAL : {
        if (isStatic(compilingMethod->access)) return false;
        uint16 index = reader.readU2();
        CommonClass* cl = NULL;
        JavaMethod* meth = NULL;
        ctpInfo->infoOfMethod(index, ACC_VIRTUAL, cl, meth);
        i += 2;
        if (meth == NULL) return false;
        if (getReceiver(stack, meth->getSignature()) != ALOAD_0) return false;
        bool customized = false;
        if (!(isFinal(cl->access) || isFinal(meth->access))) {
          if (customizeFor == NULL) return false;
          if (!customizeFor->isSubclassOf(cl)) return false;
          meth = customizeFor->lookupMethodDontThrow(
              meth->name, meth->type, false, true, NULL);
          assert(meth);
          assert(!meth->classDef->isInterface());
          assert(!isAbstract(meth->access));
          customized = true;
        }
        if (!canBeInlined(meth, customized)) return false;
        updateStack(stack, meth->getSignature(), bytecode);
        break;
      }

      case INVOKESPECIAL : {
        if (isStatic(compilingMethod->access)) return false;
        uint16 index = reader.readU2();
        CommonClass* cl = NULL;
        JavaMethod* meth = NULL;
        ctpInfo->infoOfMethod(index, ACC_VIRTUAL, cl, meth);
        i += 2;
        if (meth == NULL) return false;
        if (getReceiver(stack, meth->getSignature()) != ALOAD_0) return false;
        if (!canBeInlined(meth, false)) return false;
        updateStack(stack, meth->getSignature(), bytecode);
        break;
      }

      case INVOKESTATIC : {
        uint16 index = reader.readU2();
        CommonClass* cl = NULL;
        JavaMethod* meth = NULL;
        ctpInfo->infoOfMethod(index, ACC_STATIC, cl, meth);
        i += 2;
        if (meth == NULL) return false;
        if (!canBeInlined(meth, false)) return false;
        if (needsInitialisationCheck(cl->asClass())) return false;
        updateStack(stack, meth->getSignature(), bytecode);
        break;
      }
      
      case INVOKEINTERFACE :
        i += 4;
        return false;

      case NEW :
        i += 2;
        return false;

      case NEWARRAY :
        ++i;
        return false;
      
      case ANEWARRAY :
        i += 2;
        return false;

      case ARRAYLENGTH :
        stack.pop_back();
        stack.push_back(bytecode);
        return false;

      case ATHROW :
        return false;

      case CHECKCAST :
        i += 2;
        return false;

      case INSTANCEOF :
        i += 2;
        return false;
      
      case MONITORENTER :
      case MONITOREXIT :
        return false;
      
      case MULTIANEWARRAY :
        i += 3;
        return false;

      case WIDE :
        wide = true;
        break;

      case IFNULL :
      case IFNONNULL :
        stack.pop_back();
        i += 2;
        if (stack.size() > 0) return false;
        break;

      default :
        return false;
    }
  }
  return true;
}
