package s3.services.simplejit.ir;

import ovm.core.OVMBase;
import ovm.core.repository.Selector;
import ovm.core.repository.TypeName;
import ovm.core.domain.Blueprint;
import ovm.core.domain.Domain;
import ovm.core.domain.LinkageException;
import ovm.core.domain.Method;
import ovm.core.domain.Type;
import ovm.core.repository.Constants;
import ovm.core.repository.Descriptor;
import ovm.core.repository.JavaNames;
import ovm.core.repository.Repository;
import ovm.core.repository.RepositoryUtils;
import ovm.core.repository.TypeCodes;
import ovm.core.repository.UnboundSelector;
import ovm.services.bytecode.Instruction;
import ovm.services.bytecode.InstructionBuffer;
import ovm.services.bytecode.JVMConstants;
import ovm.services.bytecode.analysis.AbstractValue;
import ovm.services.bytecode.analysis.Frame;
import ovm.services.bytecode.analysis.State;
import ovm.util.HTint2Object;
import ovm.util.OVMError;
import ovm.util.Arrays;
import org.ovmj.util.Runabout;
import s3.core.domain.S3Constants;
import s3.core.domain.S3ConstantsBuilder;
import s3.core.domain.S3Blueprint;
import s3.core.domain.S3ByteCode;
import s3.core.domain.S3Method;
import s3.services.bytecode.verifier.VerificationInterpreter;
import s3.services.simplejit.CompilerVMInterface;
import s3.services.simplejit.SimpleJITLogger;
import s3.services.simplejit.bytecode.SimpleJITEphemeralTypeMap;
import s3.services.simplejit.bytecode.SimpleJITValue.SimpleJITJumpTarget;
import s3.services.simplejit.bytecode.SimpleJITValue;
import s3.services.simplejit.ir.SimpleJITInstruction.*;
import s3.services.simplejit.ir.SimpleJITOperand.*;
import s3.services.simplejit.bytecode.SimpleJITAbstractInterpreter;

/**
 * Convert OvmIR/Bytecode to SimpleJIT IR.
 *
 * @author Hiroshi Yamauchi
 */
public final class SimpleJITBC2IR 
    extends VerificationInterpreter
    implements JVMConstants {

    public static class Driver {

	private SimpleJITAbstractInterpreter.Driver aid;

	public Driver(SimpleJITAbstractInterpreter.Driver aid) {
	    this.aid = aid;
	}

	public SimpleJITIRCode
	    run(S3Method method,
		CompilerVMInterface compilerVMInterface) {
	  //  String sig = method.getSelector().toString();

	    /*
		//for testing micro tests
		if (! sig.startsWith("Ltest/common/TestAbstractInterpretation;")
		    && ! sig.startsWith("Gtest/common/TestAbstractInterpretation;")) {
		    return null;
		}
	    */

	    //	System.err.println("[BC2IR " 
	    //			   + method.getSelector() + "]");

	    SimpleJITAbstractInterpreter.StateAndBlockMap 
		stateNBlocks = aid.run(method, compilerVMInterface);

	    if (stateNBlocks == null)
		return null;

	    return runBC2IR(method, 
			    compilerVMInterface, 
			    stateNBlocks);
	}

	private SimpleJITIRCode
	    runBC2IR
	    (S3Method method,
	     CompilerVMInterface compilerVMInterface,
	     SimpleJITAbstractInterpreter.StateAndBlockMap stateNBlockMap) {

	    S3ByteCode bytecode = (S3ByteCode)method.getCode(S3ByteCode.KIND);
	    
	    if (bytecode == null)
		throw new Error(method + " does not have a Bytecode");
	    if (bytecode.getBytes() == null)
		throw new Error(method + " and its bytecode " + bytecode
				+ " does not have bytecode");

	    State state = stateNBlockMap.state;
	    HTint2Object entryPC2Block = stateNBlockMap.entryPC2Block;

	    String sig = method.getSelector().toString();

	    SimpleJITBC2IR bc2ir =
		new SimpleJITBC2IR(compilerVMInterface,
				   method,
				   SimpleJITValue.bogusFactory,
				   state,
				   entryPC2Block);

	    SimpleJITBC2IRFixpointIterator bc2irfi =
		new SimpleJITBC2IRFixpointIterator
		(SimpleJITValue.bogusFactory,
		 bc2ir,
		 method,
		 InstructionBuffer.wrap(bytecode));

	    Runabout[] visitors = new Runabout[] { 
		bc2ir, 
		bc2irfi.getPostVisitor()
	    };

	    try {
		for(Instruction i = bc2irfi.next(); 
		    i != null; 
		    i = bc2irfi.next()) {
		    for(int j = 0; j < visitors.length; j++)
			visitors[j].visitAppropriate(i);
		}
	
	    } catch(SimpleJITBC2IR.UnwrittenCodeError e) {
		System.err.println(e.toString());
		System.err.println("CURRENT METHOD = " + sig);
		e.printStackTrace();
		System.exit(1);
	    } catch(SimpleJITBC2IR.TypeError e) {
		bc2ir.printLogToStdErr();
		System.err.println(e.toString());
		System.err.println("CURRENT METHOD = " + sig);
		e.printStackTrace();
		System.exit(1);
	    } catch(SimpleJITBC2IR.NeedsFiatError e) {
		bc2ir.printLogToStdErr();
		System.err.println(e.toString());
		System.err.println("CURRENT METHOD = " + sig);
		e.printStackTrace();
		System.exit(1);
	    } catch(ovm.core.domain.LinkageException.Runtime e) {
		bc2ir.printLogToStdErr();
		System.err.println(e.toString());
		System.err.println("CURRENT METHOD = " + sig);
		e.printStackTrace();
	    } catch(Throwable t) {
		bc2ir.printLogToStdErr();
		System.err.println(t.toString());
		System.err.println("CURRENT METHOD = " + sig);
		bc2ir.printOpStackToStdErr();
		t.printStackTrace();
		System.exit(1);
	    }

	    if (LOG_ENABLED)
		bc2ir.printAllBlocks();

	    // Make a copy of the CP
	    S3Constants copyOfCP = (S3Constants)new S3ConstantsBuilder
        (method.getDeclaringType(),
         (S3Constants)bytecode.getConstantPool()).build();

	    SimpleJITIRCode ircode = new SimpleJITIRCode(method, 
							 copyOfCP,
							 bc2ir.getBlockSet());

	    return ircode;
	}


    }

    // Thrown when instructions that failed to be rewritten properly are seen
    public static class UnwrittenCodeError extends Error {
	public UnwrittenCodeError() {
	}
	public UnwrittenCodeError(String m) {
	    super(m);
	}
    }

    // Thrown when we hit the instructions (Quicks) that cannot be
    // abstract-interpreted properly without Fiat
    // This is temporary until Fiat gets implemented
    public static class NeedsFiatError extends Error {
	public NeedsFiatError() {
	}
	public NeedsFiatError(String m) {
	    super(m);
	}
    }

    // Thrown when incoming values are type-incorrect
    public static class TypeError extends Error {
	public TypeError() {
	}
	public TypeError(String m) {
	    super(m);
	}
    }

    /** This option enables logging in this abstract intepreter, if
     * this is turned on, the abstract interpretation gets way
     * slow. */
    public static final boolean LOG_ENABLED = false;

    /** If this option is enabled, full log messages will be logged,
     * whereas if it is disabled, only certain amount of the latest
     * log messages will be logged. This option is meaningful only
     * when LOG_ENABLED == true */
    private static final boolean FULL_LOG = false;

    /** This option specifies the size of the log message buffer when
     * fullLog == false */
    private static final int LOG_SIZE = 8 * 8192;

    protected Domain executiveDomain;
    protected Domain targetDomain; // The domain to which the method belongs
    protected Constants cp;
    protected Method method;
    protected S3Blueprint declaringTypeBP;
    protected UnboundSelector.Method[] invokeNativeList;
    private SimpleJITLogger logger;
    protected Blueprint stringBP;
    protected Blueprint contextBP;
    protected Repository repository;
    protected SimpleJITEphemeralTypeMap ephemeralTypeMap;
    protected S3ByteCode bytecode;

    HTint2Object entryPC2Block;
    protected SimpleJITBasicBlock currentBlock;
    State state;
    protected SimpleJITOperand.Stack op_stack;
    protected SimpleJITOperand.TemporaryFactory tempFactory;

    public SimpleJITBC2IR(CompilerVMInterface compilerVMInterface,
			  S3Method m,
			  AbstractValue.Factory avf,
			  State state,
			  HTint2Object entryPC2Block) {
	super(InstructionBuffer.wrap(m.getCode(S3ByteCode.KIND)),
	      avf);
	method = m;
	Type.Scalar declaringType = (Type.Scalar)m.getDeclaringType();
	targetDomain = compilerVMInterface.getTargetDomain();
	executiveDomain = compilerVMInterface.getExecutiveDomain();
	bytecode = (S3ByteCode)m.getCode(S3ByteCode.KIND);
	cp = bytecode.getConstantPool();
	declaringTypeBP = blueprintFor(targetDomain, declaringType);
	this.invokeNativeList = compilerVMInterface.getNativeMethodList();
	this.logger = 
	    FULL_LOG 
	    ? new SimpleJITLogger("SJBC2IR", System.err)
	    : new SimpleJITLogger("SJBC2IR", LOG_SIZE);
	stringBP = 
	    blueprintFor(targetDomain, JavaNames.java_lang_String);
	contextBP = 
	    blueprintFor
	    (executiveDomain, 
	     RepositoryUtils.makeTypeName("ovm/core/execution", "Context"));
	ephemeralTypeMap =
	    new SimpleJITEphemeralTypeMap(targetDomain);
	this.entryPC2Block = entryPC2Block;
	currentBlock = (SimpleJITBasicBlock)entryPC2Block.get(0);
	int maxStack = bytecode.getMaxStack();
	op_stack = new SimpleJITOperand.Stack((maxStack + 3) * 2);
	currentBlock.setOpStack(op_stack);
	this.state = state;
	tempFactory = new SimpleJITOperand.TemporaryFactory();
	appendGetArgs();
    }

    private void appendGetArgs() {
	Descriptor.Method desc = method.getSelector().getDescriptor();
	int argCount = desc.getArgumentCount();
	int local_word_counter = 0;
	SimpleJITLocalTemporaryOperand receiver =
	    makeLocalTemporary(TypeCodes.REFERENCE, local_word_counter);
	SimpleJITInstruction ins = 
	    new GetArg(TypeCodes.REFERENCE, receiver, 0);
	currentBlock.append(ins);
	local_word_counter++;
	for(int i = 0; i < argCount; i++) {
	    TypeName tn = desc.getArgumentType(i);
	    char tag = standardizeTag(tn.getTypeTag());
	    SimpleJITLocalTemporaryOperand op =
		makeLocalTemporary(tag, local_word_counter);
	    SimpleJITInstruction inst =
		new GetArg(tag, op, i + 1);
	    currentBlock.append(inst);
	    if (desc.argumentWordSize(i) == 2)
		local_word_counter += 2;
	    else
		local_word_counter++;
	}
    }

    public SimpleJITOperand.Stack getOpStack() {
	return op_stack;
    }

    private void __assert__(boolean shouldBeTrue) {
	OVMBase.OVMassert(shouldBeTrue);
    }

    private void log(String message) {
	if (LOG_ENABLED)
	    logger.addLog(message);
    }

    void addLogByFixpointIterator(String message) {
	logger.addLog(message);
    }

    public void addLogByDataFlowAnalyzer(String message) {
	logger.addLog(message);
    }

    public void printLogToStdErr() {
	logger.printToStdErr();
    }

    private String intArray2String(int[] iarray) {
	String ret = "[ ";
	for(int i = 0; i < iarray.length; i++)
	    ret += iarray[i] + " ";
	return ret + "]";
    }

    public void printOpStackToStdErr() {
	System.err.println("PC = " + getPC() + "\n"
			   + op_stack.toString());
    }

    public void printAllBlocks() {
	int[] entryPCs = entryPC2Block.keys();
	Arrays.sort(entryPCs);
	log("EntryPC list : " + intArray2String(entryPCs));
	int BClength = bytecode.getBytes().length;
	for(int j = 0; j < entryPCs.length; j++) {
	    SimpleJITBasicBlock b = 
		(SimpleJITBasicBlock)entryPC2Block.get(entryPCs[j]);
	    log(b.toString() + " : ");
	    log(b.getInEdgesAsString());
	    log(b.getOutEdgesAsString());
	    log(b.getInstructionsAsString());
	}
    }

    public S3Blueprint 
	blueprintFor(Domain domain, TypeName.Compound n) {
	try {
	    return (S3Blueprint)domain
		.blueprintFor(n, domain.getApplicationTypeContext());
	} catch (LinkageException e) {
	    return null;
	}
    }

    public S3Blueprint blueprintFor(Domain domain,
				    TypeName.Compound n, 
				    Type.Context tcontext) {
	try {
	    return (S3Blueprint)domain
		.blueprintFor(n, tcontext);
	} catch (LinkageException e) {
	    return null;
	}
    }

    Type typeFor(Domain domain,
		 TypeName.Compound n, 
		 Type.Context tcontext) {
	try {
	    return ((S3Blueprint)domain
		    .blueprintFor(n, tcontext)).getType();
	} catch (LinkageException e) {
	    return null;
	}
    }

    Type typeFor(Domain domain, TypeName.Compound n) {
	try {
	    return ((S3Blueprint)domain
		    .blueprintFor(n, 
				  targetDomain
				  .getApplicationTypeContext()))
		.getType();
	} catch (LinkageException e) {
	    return null;
	}
    }

    S3Blueprint blueprintFor(Domain domain, Type.Compound n) {
	try {
	    return (S3Blueprint)domain.blueprintFor(n);
	} catch (LinkageException e) {
	    return null;
	}
    }

    SimpleJITStackTemporaryOperand
	makeStackTemporary(char typeCode, int index) {
	return tempFactory.createStackTemporary(typeCode,index);
    }

    private SimpleJITLocalTemporaryOperand
	makeLocalTemporary(char typeCode, int index) {
	return tempFactory.createLocalTemporary(typeCode, index);
    }

    private SimpleJITConditionOperand
	branchOpcode2Condition(int opcode) {
	SimpleJITConditionOperand cond = null;
	switch(opcode) {
	case Opcodes.IF_ICMPEQ:
	case Opcodes.IFEQ:
	    cond = SimpleJITConditionOperand.eq();
	    break;
	case Opcodes.IF_ICMPNE:
	case Opcodes.IFNE:
	    cond = SimpleJITConditionOperand.ne();
	    break;
	case Opcodes.IF_ICMPLT:
	case Opcodes.IFLT:
	    cond = SimpleJITConditionOperand.lt();
	    break;
	case Opcodes.IF_ICMPGE:
	case Opcodes.IFGE:
	    cond = SimpleJITConditionOperand.ge();
	    break;
	case Opcodes.IF_ICMPGT:
	case Opcodes.IFGT:
	    cond = SimpleJITConditionOperand.gt();
	    break;
	case Opcodes.IF_ICMPLE:
	case Opcodes.IFLE:
	    cond = SimpleJITConditionOperand.le();
	    break;
	case Opcodes.IF_ACMPEQ:
	case Opcodes.IFNULL:
	    cond = SimpleJITConditionOperand.eq();
	    break;
	case Opcodes.IF_ACMPNE:
	case Opcodes.IFNONNULL:
	    cond = SimpleJITConditionOperand.ne();
	    break;
	default:
	    throw new OVMError();
	}
	return cond;
    }

    protected char standardizeTag(char tag) {
	switch(tag) {
	case TypeCodes.OBJECT:
	case TypeCodes.ARRAY:
	case TypeCodes.GEMEINSAM:
	    return TypeCodes.REFERENCE;
	case TypeCodes.INT:
	case TypeCodes.BYTE:
	case TypeCodes.CHAR:
	case TypeCodes.BOOLEAN:
	case TypeCodes.SHORT:
	    return TypeCodes.INT;
	case TypeCodes.LONG:
	case TypeCodes.DOUBLE:
	case TypeCodes.FLOAT:
	case TypeCodes.VOID:
	case TypeCodes.RETURNADDRESS:
	case TypeCodes.REFERENCE:
	    return tag;
	default:
	    throw new Error("tag = " + tag);
	}
    }

    protected char opcode2TypeCode(int opcode) {
	char type = 0;
	switch(opcode) {
	case Opcodes.ISTORE_0: case Opcodes.ISTORE_1: 
	case Opcodes.ISTORE_2: case Opcodes.ISTORE_3: case Opcodes.ISTORE: 
	case Opcodes.ILOAD_0: case Opcodes.ILOAD_1: 
	case Opcodes.ILOAD_2: case Opcodes.ILOAD_3: case Opcodes.ILOAD: 
	case Opcodes.IF_ICMPEQ:	case Opcodes.IFEQ: 
	case Opcodes.IF_ICMPNE: case Opcodes.IFNE: 
	case Opcodes.IF_ICMPLT:	case Opcodes.IFLT: 
	case Opcodes.IF_ICMPGE: case Opcodes.IFGE: 
	case Opcodes.IF_ICMPGT: case Opcodes.IFGT: 
	case Opcodes.IF_ICMPLE: case Opcodes.IFLE:
	case Opcodes.IALOAD:
	case Opcodes.IASTORE:
	    type = TypeCodes.INT; 
	    break;
	case Opcodes.LSTORE_0: case Opcodes.LSTORE_1: 
	case Opcodes.LSTORE_2: case Opcodes.LSTORE_3: case Opcodes.LSTORE: 
	case Opcodes.LLOAD_0: case Opcodes.LLOAD_1: 
	case Opcodes.LLOAD_2: case Opcodes.LLOAD_3: case Opcodes.LLOAD: 
	case Opcodes.LALOAD:
	case Opcodes.LASTORE:
	    type = TypeCodes.LONG; 
	    break;
	case Opcodes.FSTORE_0: case Opcodes.FSTORE_1: 
	case Opcodes.FSTORE_2: case Opcodes.FSTORE_3: case Opcodes.FSTORE: 
	case Opcodes.FLOAD_0: case Opcodes.FLOAD_1: 
	case Opcodes.FLOAD_2: case Opcodes.FLOAD_3: case Opcodes.FLOAD: 
	case Opcodes.FALOAD:
	case Opcodes.FASTORE:
	    type = TypeCodes.FLOAT; 
	    break;
	case Opcodes.DSTORE_0: case Opcodes.DSTORE_1: 
	case Opcodes.DSTORE_2: case Opcodes.DSTORE_3: case Opcodes.DSTORE: 
	case Opcodes.DLOAD_0: case Opcodes.DLOAD_1: 
	case Opcodes.DLOAD_2: case Opcodes.DLOAD_3: case Opcodes.DLOAD: 
	case Opcodes.DALOAD:
	case Opcodes.DASTORE:
	    type = TypeCodes.DOUBLE; 
	    break;
	case Opcodes.ASTORE_0: case Opcodes.ASTORE_1: 
	case Opcodes.ASTORE_2: case Opcodes.ASTORE_3: case Opcodes.ASTORE: 
	case Opcodes.ALOAD_0: case Opcodes.ALOAD_1: 
	case Opcodes.ALOAD_2: case Opcodes.ALOAD_3: case Opcodes.ALOAD: 
	case Opcodes.IF_ACMPEQ:	case Opcodes.IFNULL: 
	case Opcodes.IF_ACMPNE: case Opcodes.IFNONNULL:
	case Opcodes.AALOAD:
	case Opcodes.AASTORE:
	case Opcodes.UNCHECKED_AASTORE:
	case Opcodes.AASTORE_WITH_BARRIER:
	    type = TypeCodes.REFERENCE; 
	    break;
	case Opcodes.SALOAD:
	case Opcodes.SASTORE:
	    type = TypeCodes.SHORT; 
	    break;
	case Opcodes.CALOAD:
	case Opcodes.CASTORE:
	    type = TypeCodes.CHAR; 
	    break;
	case Opcodes.BALOAD:
	case Opcodes.BASTORE:
	    type = TypeCodes.BYTE; 
	    break;
	default:
	    throw new OVMError("Unexpected opcode : " + opcode);
	}
	return type;
    }

    public SimpleJITBasicBlock.Set getBlockSet() {
	return new SimpleJITBasicBlock.Set(entryPC2Block);
    }

    public SimpleJITBasicBlock getBlockAt(int pc) {
	return (SimpleJITBasicBlock)entryPC2Block.get(pc);
    }

    void setPC(int pc) {
	setFrameAndPC(null, pc);
    }
    public SimpleJITBasicBlock getCurrentBlock() {
	return currentBlock;
    }
    public SimpleJITOperand.Stack getCurrentOpStack() {
	return op_stack;
    }
    public boolean isBlockStart(int pc) {
	Object b = entryPC2Block.get(pc);
	return b != null;
    }
    public void setCurrentBlockAndOpStackByPC(int pc) {
	Object b = entryPC2Block.get(pc);
	if (b != null) {
	    currentBlock = (SimpleJITBasicBlock)b;
	    op_stack = currentBlock.getOpStack();
	    if (currentBlock.isHandlerBlock()) {
		SimpleJITStackTemporaryOperand sop = 
		    makeStackTemporary(TypeCodes.REFERENCE, 0);
		SimpleJITInstruction inst = new GetThrowable(sop);
		currentBlock.append(inst);
	    }
	}
    }
    public void visit(Instruction i) {
	throw new Error("visit method for " + i + " has not been implemented");
    }

    public void visit(Instruction.NOP i) {
	currentBlock.appendNothing();
    }

    public void visit(Instruction.IFIAT i) {
	SimpleJITOperand op = op_stack.pop();
	if (op instanceof SimpleJITTemporaryOperand) {
	    op_stack.push(((SimpleJITTemporaryOperand)op)
			  .copyWithNewTypeCode(tempFactory, 
					       TypeCodes.INT));
	} else {
	    op_stack.push(makeStackTemporary(TypeCodes.INT,
					     op_stack.height() + 1));
	}
	currentBlock.appendNothing();
    }
    public void visit(Instruction.FFIAT i) {
	SimpleJITOperand op = op_stack.pop();
	if (op instanceof SimpleJITTemporaryOperand) {
	    op_stack.push(((SimpleJITTemporaryOperand)op)
			  .copyWithNewTypeCode(tempFactory,
					       TypeCodes.FLOAT));
	} else {
	    op_stack.push(makeStackTemporary(TypeCodes.FLOAT,
					     op_stack.height() + 1));
	}
    }
    public void visit(Instruction.LFIAT i) {
	SimpleJITOperand op = op_stack.popWide();
	if (op instanceof SimpleJITTemporaryOperand) {
	    op_stack.pushWide(((SimpleJITTemporaryOperand)op)
			      .copyWithNewTypeCode(tempFactory,
						   TypeCodes.LONG));
	} else {
	    op_stack.pushWide
		(makeStackTemporary(TypeCodes.LONG,
				    op_stack.height() + 1));
	}
	currentBlock.appendNothing();
    }
    public void visit(Instruction.DFIAT i) {
	SimpleJITOperand op = op_stack.popWide();
	if (op instanceof SimpleJITTemporaryOperand) {
	    op_stack.pushWide(((SimpleJITTemporaryOperand)op)
			      .copyWithNewTypeCode(tempFactory,
						   TypeCodes.DOUBLE));
	} else {
	    op_stack.pushWide
		(makeStackTemporary(TypeCodes.DOUBLE,
				    op_stack.height() + 1));
	}
	currentBlock.appendNothing();
    }
    public void visit(Instruction.AFIAT i) {
	SimpleJITOperand op = op_stack.pop();
	if (op instanceof SimpleJITTemporaryOperand) {
	    op_stack.push(((SimpleJITTemporaryOperand)op)
			  .copyWithNewTypeCode(tempFactory,
					       TypeCodes.REFERENCE));
	} else {
	    op_stack.push(makeStackTemporary(TypeCodes.REFERENCE,
					     op_stack.height() + 1));
	}
	currentBlock.appendNothing();
    }
    public void visit(Instruction.ConstantPoolLoad i) {
	if (LOG_ENABLED) log(pc_ + " : ConstantPoolLoad " + i);
	int cpindex = i.getCPIndex(buf);
	SimpleJITInstruction inst = null;
	switch(cp.getTagAt(cpindex)) {
	case CONSTANT_Integer: {
	    int value = cp.getValueAt(cpindex);
	    SimpleJITIntOperand op = new SimpleJITIntOperand(value);
	    op_stack.push(op);
// 	    SimpleJITStackTemporaryOperand sop =
// 		makeStackTemporary(TypeCodes.INT, 
// 				   op_stack.height() + 1);
// 	    op_stack.push(sop);
//  	    inst = Move.create(TypeCodes.INT, 
// 			       sop,
//  			       op,
//  			       null);
	    break;
	}
	case CONSTANT_Float: {
	    float value = Float.intBitsToFloat(cp.getValueAt(cpindex));
	    SimpleJITFloatOperand op = new SimpleJITFloatOperand(value);
	    op_stack.push(op);
// 	    SimpleJITStackTemporaryOperand sop =
// 		makeStackTemporary(TypeCodes.FLOAT,
// 				   op_stack.height() + 1);
// 	    op_stack.push(sop);
//  	    inst = Move.create(TypeCodes.FLOAT,
// 			       sop,
//  			       op,
//  			       null);
	    break;
	}
	case CONSTANT_Long: {
	    long value = cp.getWideValueAt(cpindex);
	    SimpleJITLongOperand op = new SimpleJITLongOperand(value);
	    op_stack.pushWide(op);
// 	    SimpleJITStackTemporaryOperand sop =
// 		makeStackTemporary(TypeCodes.LONG,
// 				   op_stack.height() + 1);
// 	    op_stack.push(sop);
//  	    inst = Move.create(TypeCodes.LONG,
// 			       sop,
//  			       op,
//  			       null);
	    break;
	}
	case CONSTANT_Double: {
	    double value = 
		Double.longBitsToDouble(cp.getWideValueAt(cpindex));
	    SimpleJITDoubleOperand op = new SimpleJITDoubleOperand(value);
	    op_stack.pushWide(op);
// 	    SimpleJITStackTemporaryOperand sop =
// 		makeStackTemporary(TypeCodes.DOUBLE,
// 				   op_stack.height() + 1);
// 	    op_stack.push(sop);
//  	    inst = Move.create(TypeCodes.DOUBLE,
// 			       sop,
//  			       op,
//  			       null);
	    break;
	}
	case CONSTANT_String: {
	    SimpleJITConstantReferenceOperand op =
		new SimpleJITConstantReferenceOperand
		(cpindex, 
		 CONSTANT_String);
//	    op_stack.push(op);
 	    SimpleJITStackTemporaryOperand sop =
 		makeStackTemporary(TypeCodes.REFERENCE,
 				   op_stack.height() + 1);
 	    op_stack.push(sop);
//   	    inst = Move.create(TypeCodes.REFERENCE,
//  			       sop,
//   			       op,
//   			       null);
	    inst = new ConstantLoad(sop,
				    cpindex,
				    CONSTANT_String);
	    break;
	}
	case CONSTANT_Binder: {
	    SimpleJITConstantReferenceOperand op =
		new SimpleJITConstantReferenceOperand(cpindex, 
					     CONSTANT_Binder);
//	    op_stack.push(op);
 	    SimpleJITStackTemporaryOperand sop =
 		makeStackTemporary(TypeCodes.REFERENCE,
 				   op_stack.height() + 1);
 	    op_stack.push(sop);
//   	    inst = Move.create(TypeCodes.REFERENCE,
//  			       sop,
//   			       op,
//   			       null);
	    inst = new ConstantLoad(sop,
				    cpindex,
				    CONSTANT_Binder);
	    break;
	}
	case CONSTANT_Reference: {
	    SimpleJITConstantReferenceOperand op =
		new SimpleJITConstantReferenceOperand(cpindex, 
					     CONSTANT_Reference);
//	    op_stack.push(op);
 	    SimpleJITStackTemporaryOperand sop =
 		makeStackTemporary(TypeCodes.REFERENCE,
 				   op_stack.height() + 1);
 	    op_stack.push(sop);
//   	    inst = Move.create(TypeCodes.REFERENCE,
//  			       sop,
//   			       op,
//   			       null);
	    inst = new ConstantLoad(sop,
				    cpindex,
				    CONSTANT_Reference);
	    break;
	}
	case CONSTANT_SharedState: {
	    SimpleJITConstantReferenceOperand op =
		new SimpleJITConstantReferenceOperand
		(cpindex, 
		 CONSTANT_SharedState);
//	    op_stack.push(op);
 	    SimpleJITStackTemporaryOperand sop =
 		makeStackTemporary(TypeCodes.REFERENCE,
 				   op_stack.height() + 1);
 	    op_stack.push(sop);
//   	    inst = Move.create(TypeCodes.REFERENCE,
//   			       sop,
//  			       op,
//   			       null);
	    inst = new ConstantLoad(sop,
				    cpindex,
				    CONSTANT_SharedState);
	    break;
	}
	default:
	    throw new Error("Unexpected CP tag: " + cp.getTagAt(cpindex));
	}
	if (inst != null)
	    currentBlock.append(inst);
	else
	    currentBlock.appendNothing();
    }
    public void visit(Instruction.ACONST_NULL i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	op_stack.push(new SimpleJITNullReferenceOperand());
	currentBlock.appendNothing();
    }
    public void visit(Instruction.IConstantLoad i) {
	if (LOG_ENABLED) log(pc_ + " : IConstantLoad " + i);
	op_stack.push(new SimpleJITIntOperand(i.getValue(buf)));
	currentBlock.appendNothing();
    }
    public void visit(Instruction.FConstantLoad i) {
	if (LOG_ENABLED) log(pc_ + " : FConstantLoad " + i);
	op_stack.push(new SimpleJITFloatOperand(i.getFValue(buf)));
	currentBlock.appendNothing();
    }
    public void visit(Instruction.LConstantLoad i) {
	if (LOG_ENABLED) log(pc_ + " : LConstantLoad " + i);
	op_stack.pushWide(new SimpleJITLongOperand(i.getLValue(buf)));
	currentBlock.appendNothing();
    }
    public void visit(Instruction.DConstantLoad i) {
	if (LOG_ENABLED) log(pc_ + " : DConstantLoad " + i);
	op_stack.pushWide(new SimpleJITDoubleOperand(i.getDValue(buf)));
	currentBlock.appendNothing();
    }
    public void visit(Instruction.LocalRead i) {
	int local_index = i.getLocalVariableOffset(buf);
	if (LOG_ENABLED) log(pc_ + " : LocalRead " + i + " " + local_index);
	char tag = opcode2TypeCode(i.getOpcode());
	op_stack.pushWithTag(tag, makeLocalTemporary(tag, local_index));
	currentBlock.appendNothing();
    }

    public void visit(Instruction.LocalWrite i) {
	int local_index = i.getLocalVariableOffset(buf);
	if (LOG_ENABLED) log(pc_ + " : LocalWrite " + i + " " + local_index);
	char type = opcode2TypeCode(i.getOpcode());
	SimpleJITOperand l = makeLocalTemporary(type, local_index);
	SimpleJITOperand s = op_stack.popWithTag(type);
	SimpleJITInstruction last = currentBlock.getLastInstruction();
	if (currentBlock.isContiguous()
	    && last != null 
	    && last.isDefiner()) {
	    SimpleJITInstruction.Definer df = last.asDefiner();
	    SimpleJITOperand lastResult = df.getDefOperands()[0];
	    if (lastResult instanceof SimpleJITStackTemporaryOperand
		&& s instanceof SimpleJITStackTemporaryOperand
		&& lastResult.equals(s)) {
		df.setDefOperands(new SimpleJITOperand[] {l});
		return; // copy-propagated the result of the previous instruction
	    }
	}
	currentBlock.append(Move.create(type, l, s, null));
    }

    public void visit(Instruction.TABLESWITCH i) {
	if (LOG_ENABLED) log(pc_ + " : TABLESWITCH");
	int def = i.getDefaultTarget(buf);
	int high = i.getHigh(buf);
	int low = i.getLow(buf);
	int[] targets = i.getTargets(buf);
	int currentPC = getPC();
	SimpleJITOperand s = op_stack.pop();
	SimpleJITBasicBlock[] targetBlocks =
	    new SimpleJITBasicBlock[targets.length];
	for(int j = 0; j < targets.length; j++) {
	    targetBlocks[j] = 
		getBlockAt(targets[j] + currentPC);
	}
	SimpleJITBasicBlock defBlock = 
	    getBlockAt(def + currentPC);
	SimpleJITInstruction q = 
	    new TableSwitch(s,
			    low,
			    high,
			    defBlock,
			    targetBlocks);
	currentBlock.append(q);
    }

    public void visit(Instruction.LOOKUPSWITCH i) {
	if (LOG_ENABLED) log(pc_ + " : LOOKUPSWITCH");
	int def = i.getDefaultTarget(buf);
	int npairs = i.getTargetCount(buf) - 1;
	int[] cases = i.getIndexForTargets(buf);
	int[] targets = i.getTargets(buf);
	int currentPC = getPC();
	SimpleJITOperand s = op_stack.pop();
	SimpleJITBasicBlock[] targetBlocks =
	    new SimpleJITBasicBlock[targets.length];
	for(int j = 0; j < targets.length; j++) {
	    targetBlocks[j] = 
		getBlockAt(targets[j] + currentPC);
	}
	SimpleJITBasicBlock defBlock = 
	    getBlockAt(def + currentPC);
	SimpleJITInstruction q = 
	    new LookupSwitch(s,
			     npairs,
			     cases,
			     defBlock,
			     targetBlocks);
	currentBlock.append(q);
    }

    private void visitGOTOs(int target) {
	int targetPC = target + getPC();
	SimpleJITBasicBlock targetBlock = 
	    getBlockAt(targetPC);
	SimpleJITInstruction inst =
	    new Goto(targetBlock);
	currentBlock.append(inst);
    }

    public void visit(Instruction.GOTO i) {
	if (LOG_ENABLED) log(pc_ + " : GOTO " + i.getTarget(buf));
	visitGOTOs(i.getTarget(buf));
    }

    public void visit(Instruction.GOTO_W i) {
	if (LOG_ENABLED) log(pc_ + " : GOTO_W " + i.getTarget(buf));
	visitGOTOs(i.getTarget(buf));
    }

    private void visitRETs(int local_index) {
	SimpleJITLocalTemporaryOperand l = 
	    makeLocalTemporary(TypeCodes.RETURNADDRESS, local_index);
	currentBlock.append(new Ret(l));
    }

    public void visit(Instruction.RET i) {
	if (LOG_ENABLED) log(pc_ + " : RET");
	visitRETs(i.getLocalVariableOffset(buf));
    }

    public void visit(Instruction.WIDE_RET i) {
	if (LOG_ENABLED) log(pc_ + " : WIDE_RET");
	visitRETs(i.getLocalVariableOffset(buf));
    }

    private void visitJSRs(int target, int size) {
	// Subroutines will be inlined/duplicated for each JSR site in
	// a later pass
	int targetPC = target + getPC();
	SimpleJITBasicBlock b = getBlockAt(targetPC);
	op_stack.push(makeStackTemporary(TypeCodes.RETURNADDRESS, 
					 op_stack.height() + 1));
	currentBlock.append(new Jsr(b));
    }

    public void visit(Instruction.JSR i) {
	if (LOG_ENABLED) log(pc_ + " : JSR");
	visitJSRs(i.getTarget(buf), i.size(buf));
    }

    public void visit(Instruction.JSR_W i) {
	if (LOG_ENABLED) log(pc_ + " : JSR_W");
	visitJSRs(i.getTarget(buf), i.size(buf));
    }

    public void visit(Instruction.IfCmp i) {
	if (LOG_ENABLED) log(pc_ + " : IfCmp " + i);
	int targetPC = i.getBranchTarget(buf) + getPC();
	SimpleJITBasicBlock targetBlock = getBlockAt(targetPC);
	SimpleJITOperand op2 = op_stack.pop();
	SimpleJITOperand op1 = op_stack.pop();
	int opcode = i.getOpcode();
	SimpleJITConditionOperand cond = branchOpcode2Condition(opcode);
	char type = opcode2TypeCode(opcode);
	SimpleJITBasicBlock fallThoughBlock = 
	    getBlockAt(getPC() + i.size(buf));
	currentBlock.append(new IfCmp(type, cond, 
				      op1, op2, 
				      targetBlock,
				      fallThoughBlock));
    }

    public void visit(Instruction.If i) {
	if (LOG_ENABLED) log(pc_ + " : If " + i);
	int targetPC = i.getBranchTarget(buf) + getPC();
	SimpleJITBasicBlock targetBlock = 
	    getBlockAt(targetPC);
	SimpleJITBasicBlock fallThoughBlock = 
	    getBlockAt(getPC() + i.size(buf));
	SimpleJITOperand op2 = null;
	SimpleJITOperand op1 = op_stack.pop();
	int opcode = i.getOpcode();
	SimpleJITConditionOperand cond = branchOpcode2Condition(opcode);
	char type = opcode2TypeCode(opcode);
	switch(opcode) {
	case Opcodes.IFEQ:
	case Opcodes.IFNE:
	case Opcodes.IFLT:
	case Opcodes.IFGE:
	case Opcodes.IFGT:
	case Opcodes.IFLE:
	    op2 = new SimpleJITIntOperand(0);
	    break;
	case Opcodes.IFNULL:
	case Opcodes.IFNONNULL:
	    op2 = new SimpleJITNullReferenceOperand();
	    break;
	default:
	    throw new OVMError();
	}
	currentBlock
	    .append(new IfCmp(type, 
			      cond,
			      op1, 
			      op2,
			      targetBlock,
			      fallThoughBlock));
    }

    public void visit(Instruction.IALOAD i) {
	visit((Instruction.ArrayLoad)i);
    }
    public void visit(Instruction.LALOAD i) {
	visit((Instruction.ArrayLoad)i);
    }
    public void visit(Instruction.FALOAD i) {
	visit((Instruction.ArrayLoad)i);
    }
    public void visit(Instruction.DALOAD i) {
	visit((Instruction.ArrayLoad)i);
    }
    public void visit(Instruction.AALOAD i) {
	visit((Instruction.ArrayLoad)i);
    }
    public void visit(Instruction.CALOAD i) {
	visit((Instruction.ArrayLoad)i);
    }
    public void visit(Instruction.SALOAD i) {
	visit((Instruction.ArrayLoad)i);
    }
    public void visit(Instruction.BALOAD i) {
	visit((Instruction.ArrayLoad)i);
    }

    public void visit(Instruction.ArrayLoad i) {
	if (LOG_ENABLED) log(pc_ + " : ArrayLoad " + i);
	SimpleJITOperand indexOperand = op_stack.pop();
	SimpleJITOperand arrayOperand = op_stack.pop();
	int opcode = i.getOpcode();
	char org_tag = opcode2TypeCode(opcode);
	char tag = standardizeTag(org_tag);
	SimpleJITOperand resultOperand = makeStackTemporary(tag, op_stack.height() + 1);
	op_stack.pushWithTag(tag, resultOperand);
	if (tag == TypeCodes.REFERENCE) {
	  currentBlock.append(new RefArrayLoad(resultOperand, arrayOperand, indexOperand));
	}
	  currentBlock.append(new ArrayLoad(org_tag, resultOperand, arrayOperand, indexOperand));
    }

    public void visit(Instruction.ArrayStore i) {
	if (LOG_ENABLED) log(pc_ + " : ArrayStore " + i);
	int opcode = i.getOpcode();
	char tag = standardizeTag(opcode2TypeCode(opcode));
	SimpleJITOperand valueOperand = op_stack.popWithTag(tag);
	SimpleJITOperand indexOperand = op_stack.pop();
	SimpleJITOperand arrayOperand = op_stack.pop();
	if (tag == TypeCodes.REFERENCE)
	    switch(opcode) {
	    case Opcodes.AASTORE:
		currentBlock.append
		    (new RefArrayStore(arrayOperand, indexOperand, valueOperand));
		break;
	    case Opcodes.UNCHECKED_AASTORE:
		currentBlock.append
		    (new UncheckedRefArrayStore(arrayOperand, indexOperand, valueOperand));
		break;
	    case Opcodes.AASTORE_WITH_BARRIER:
		currentBlock.append
		    (new RefArrayStoreWithBarrier(arrayOperand, indexOperand, valueOperand));
		break;
	    default:
		throw new OVMError("Unexpected array store opcode");
	    }
	else
	    currentBlock.append(new ArrayStore(tag, arrayOperand, indexOperand, valueOperand));
    }

    private void visitBinaryOp(char type, int operator) {
	visitBinaryOp(type, type, type, operator);
    }

    private void visitBinaryOp(char result_type, char op1_type, char op2_type, int operator) {
	SimpleJITOperand operand2 = op_stack.popWithTag(op2_type);
	SimpleJITOperand operand1 = op_stack.popWithTag(op1_type);
	SimpleJITOperand resultOperand = makeStackTemporary(result_type, op_stack.height() + 1);
	op_stack.pushWithTag(result_type, resultOperand);
	currentBlock.append(new BinaryOp(operator, resultOperand, operand1, operand2));
    }

    private void visitUnaryOp(char desttype, char srctype, int operator) {
	SimpleJITOperand operand = op_stack.popWithTag(srctype);
	SimpleJITOperand resultOperand = makeStackTemporary(desttype, op_stack.height() + 1);
	op_stack.pushWithTag(desttype, resultOperand);
	currentBlock.append(new UnaryOp(operator, resultOperand, operand));
    }

    public void visit(Instruction.ARRAYLENGTH i) {
	if (LOG_ENABLED) log(pc_ + " : ARRAYLENGTH " + i);
	SimpleJITOperand operand = op_stack.pop();
	SimpleJITOperand resultOperand = makeStackTemporary(TypeCodes.INT, op_stack.height() + 1);
	op_stack.push(resultOperand);
	currentBlock.append(new ArrayLength(resultOperand, operand));
    }

    public void visitInvocation(Instruction.Invocation i, int operator) {
	Selector.Method sel = i.getSelector(buf, cp);
	Descriptor.Method desc = sel.getDescriptor();
	int formalArgumentCount = desc.getArgumentCount();
	SimpleJITOperand[] ops = 
	    new SimpleJITOperand[formalArgumentCount + 1];
	for(int j = formalArgumentCount - 1; j >= 0; j--) {
	    char tag = desc.getArgumentType(j).getTypeTag();
	    ops[j + 1] = op_stack.popWithTag(tag);
	}
	ops[0] = op_stack.pop();// receiver
	char rttag = standardizeTag(desc.getType().getTypeTag());
	if (rttag != TypeCodes.VOID) {
	    SimpleJITOperand resultOperand = makeStackTemporary(rttag, op_stack.height() + 1);
	    op_stack.pushWithTag(rttag, resultOperand);
	    currentBlock.append(new CallNonVoid(operator, resultOperand, ops, sel, i.getCPIndex(buf)));
	} else {
	    currentBlock.append(new CallVoid(operator, ops, sel, i.getCPIndex(buf)));
	}
    }

    public void visitInvocationQuick(Instruction.Invocation_Quick i, int operator) {
	Selector.Method sel = i.getSelector(buf, cp);
	Descriptor.Method desc = sel.getDescriptor();
	int formalArgumentCount = desc.getArgumentCount();
	SimpleJITOperand[] ops = 
	    new SimpleJITOperand[formalArgumentCount + 1];
	for(int j = formalArgumentCount - 1; j >= 0; j--) {
	    char tag = desc.getArgumentType(j).getTypeTag();
	    ops[j + 1] = op_stack.popWithTag(tag);
	}
	ops[0] = op_stack.pop();// receiver
	char rttag = standardizeTag(desc.getType().getTypeTag());
	if (rttag != TypeCodes.VOID) {
	    SimpleJITOperand resultOperand = makeStackTemporary(rttag, op_stack.height() + 1);
	    op_stack.pushWithTag(rttag, resultOperand);
	    currentBlock.append(new CallNonVoid(operator, resultOperand, ops, sel));
	} else {
	    currentBlock.append(new CallVoid(operator, ops, sel));
	}
    }

    public void visit(Instruction.INVOKEVIRTUAL i) {
	if (LOG_ENABLED) log(pc_ + " : INVOKEVIRTUAL");
	visitInvocation(i, Operators.INVOKEVIRTUAL);
    }
    public void visit(Instruction.INVOKESPECIAL i) {
	if (LOG_ENABLED) log(pc_ + " : INVOKESPECIAL");
	visitInvocation(i, Operators.INVOKESPECIAL);
    }

    public void visit(Instruction.INVOKESTATIC i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	Selector.Method sel = i.getSelector(buf, cp);
	Descriptor.Method desc = sel.getDescriptor();
	int formalArgumentCount = desc.getArgumentCount();
	SimpleJITOperand[] ops = 
	    new SimpleJITOperand[formalArgumentCount];
	for(int j = formalArgumentCount - 1; j >= 0; j--) {
	    char tag = desc.getArgumentType(j).getTypeTag();
	    ops[j] = op_stack.popWithTag(tag);
	}
	char rttag = standardizeTag(desc.getType().getTypeTag());
	if (rttag != TypeCodes.VOID) {
	    SimpleJITOperand resultOperand = makeStackTemporary(rttag, op_stack.height() + 1);
	    op_stack.pushWithTag(rttag, resultOperand);
	    currentBlock.append(new CallNonVoid(Operators.INVOKESTATIC, resultOperand, ops, sel, i.getCPIndex(buf)));
	} else {
	    currentBlock.append(new CallVoid(Operators.INVOKESTATIC, ops, sel, i.getCPIndex(buf)));
	}
    }

    public void visit(Instruction.INVOKEINTERFACE i) {
	if (LOG_ENABLED) log(pc_ + " : INVOKEINTERFACE");
	visitInvocation(i, Operators.INVOKEINTERFACE);
    }

    public void visit(Instruction.INVOKE_NATIVE i) {
	if (LOG_ENABLED) log(pc_ + " : INVOKE_NATIVE");
	int mindex = i.getMethodIndex(buf);
	UnboundSelector.Method sel = invokeNativeList[mindex];
	Descriptor.Method desc = sel.getDescriptor();
	int formalArgumentCount = desc.getArgumentCount();
	SimpleJITOperand[] ops = 
	    new SimpleJITOperand[formalArgumentCount];
	for(int j = formalArgumentCount - 1; j >= 0; j--) {
	    char tag = desc.getArgumentType(j).getTypeTag();
	    ops[j] = op_stack.popWithTag(tag);
	}
	char rttag = standardizeTag(desc.getType().getTypeTag());
	if (rttag != TypeCodes.VOID) {
	    SimpleJITOperand resultOperand = makeStackTemporary(rttag, op_stack.height() + 1);
	    op_stack.pushWithTag(rttag, resultOperand);
	    currentBlock.append(new NativeCallNonVoid(mindex, resultOperand, ops, sel));
	} else {
	    currentBlock.append(new NativeCallVoid(mindex, ops, sel));
	}
    }

    public void visit(Instruction.INVOKE_SYSTEM i) {
	if (LOG_ENABLED) log(pc_ + " : INVOKE_SYSTEM");
	int mindex = i.getMethodIndex(buf);
	int optype = i.getOpType(buf);
	SimpleJITInstruction ins = null;
	SimpleJITOperand result, arg1, arg2, arg3, arg4;
	switch(mindex) {
	case InvokeSystemArguments.BREAKPOINT:
	    arg1 = op_stack.pop();
	    ins = new SystemCallVoid(mindex, optype, new SimpleJITOperand[] { arg1 });
	    break;
	case InvokeSystemArguments.RUN:
	case InvokeSystemArguments.CUT_TO_ACTIVATION:
	    arg2 = op_stack.pop();
	    arg1 = op_stack.pop();
	    ins = new SystemCallVoid(mindex, optype, new SimpleJITOperand[] { arg1, arg2 });
	    break;
	case InvokeSystemArguments.EMPTY_CSA_CALL:
	case InvokeSystemArguments.STOP_TRACING:
	case InvokeSystemArguments.BEGIN_OVERRIDE_TRACING:
	case InvokeSystemArguments.END_OVERRIDE_TRACING:
	    ins = new SystemCallVoid(mindex, optype, new SimpleJITOperand[0]);
	    break;
	case InvokeSystemArguments.GET_CONTEXT:
	    arg1 = op_stack.pop();
	    result = makeStackTemporary(TypeCodes.REFERENCE, op_stack.height() + 1);
	    op_stack.push(result);
	    ins = new SystemCallNonVoid(mindex, optype, result, new SimpleJITOperand[] { arg1 });
	    break;
	case InvokeSystemArguments.NEW_CONTEXT:
	    arg1 = op_stack.pop();
	    result = makeStackTemporary(TypeCodes.INT, op_stack.height() + 1);
	    op_stack.push(result);
	    ins = new SystemCallNonVoid(mindex, optype, result, new SimpleJITOperand[] { arg1 });
	    break;
	case InvokeSystemArguments.DESTROY_NATIVE_CONTEXT:
	    arg1 = op_stack.pop();
	    ins = new SystemCallVoid(mindex, optype, new SimpleJITOperand[] { arg1 });
	    break;
	case InvokeSystemArguments.GET_ACTIVATION:
	case InvokeSystemArguments.START_TRACING:
	    arg1 = op_stack.pop();
	    result = makeStackTemporary(TypeCodes.INT, op_stack.height() + 1);
	    ins = new SystemCallNonVoid(mindex, optype, result, new SimpleJITOperand[] { arg1 });
	    op_stack.push(result);
	    break;
	case InvokeSystemArguments.MAKE_ACTIVATION:
	    arg3 = op_stack.pop();
	    arg2 = op_stack.pop();
	    arg1 = op_stack.pop();
	    result = makeStackTemporary(TypeCodes.INT, op_stack.height() + 1);
	    op_stack.push(result);
	    ins = new SystemCallNonVoid(mindex, optype, result, 
					new SimpleJITOperand[] { arg1, arg2, arg3 });
	    break;
	case InvokeSystemArguments.INVOKE:
	    arg2 = op_stack.pop();
	    arg1 = op_stack.pop();
	    switch(optype) {
	    case TypeCodes.VOID:
		ins = new SystemCallVoid(mindex, optype, new SimpleJITOperand[] { arg1, arg2 });
		break;
	    case TypeCodes.OBJECT:
		result = makeStackTemporary(TypeCodes.REFERENCE, op_stack.height() + 1);
		op_stack.push(result);
		ins = new SystemCallNonVoid(mindex, optype, result, 
					    new SimpleJITOperand[] { arg1, arg2 });
		break;
	    case TypeCodes.FLOAT:
		result = makeStackTemporary(TypeCodes.FLOAT, op_stack.height() + 1);
		op_stack.push(result);
		ins = new SystemCallNonVoid(mindex, optype, result, 
					    new SimpleJITOperand[] { arg1, arg2 });
		break;
	    case TypeCodes.DOUBLE:
		result = makeStackTemporary(TypeCodes.DOUBLE, op_stack.height() + 1);
		op_stack.pushWide(result);
		ins = new SystemCallNonVoid(mindex, optype, result, 
					    new SimpleJITOperand[] { arg1, arg2 });
		break;
	    case TypeCodes.LONG:
		result = makeStackTemporary(TypeCodes.LONG, op_stack.height() + 1);
		op_stack.pushWide(result);
		ins = new SystemCallNonVoid(mindex, optype, result, 
					    new SimpleJITOperand[] { arg1, arg2 });
		break;
	    case TypeCodes.INT:
		result = makeStackTemporary(TypeCodes.INT, op_stack.height() + 1);
		op_stack.push(result);
		ins = new SystemCallNonVoid(mindex, optype, result, 
					    new SimpleJITOperand[] { arg1, arg2 });
		break;
	    default:
		throw new OVMError("Unexpected type tag");
	    }
	    break;
	case InvokeSystemArguments.WORD_OP: {
	    switch(optype) {
	    case WordOps.uI2L:
		arg1 = op_stack.pop();
		result = makeStackTemporary(TypeCodes.LONG, op_stack.height() + 1);
		op_stack.pushWide(result);
		ins = new SystemCallNonVoid(mindex, optype, result, new SimpleJITOperand[] { arg1 });
		break;
	    case WordOps.sCMP:
	    case WordOps.uCMP:
	    case WordOps.uLT:
	    case WordOps.uLE:
	    case WordOps.uGE:
	    case WordOps.uGT:
		arg2 = op_stack.pop();
		arg1 = op_stack.pop();
		result = makeStackTemporary(TypeCodes.INT, op_stack.height() + 1);
		op_stack.push(result);
		ins = new SystemCallNonVoid(mindex, optype, result, 
					    new SimpleJITOperand[] { arg1, arg2 });
		break;
	    default:
		throw new OVMError
		    ("Unsupported invoke_system wordop argument : " + optype);
	    }
	    break;
	}
	case InvokeSystemArguments.DEREFERENCE: {
	    switch(optype) {
	    case DereferenceOps.getByte:
	    case DereferenceOps.getShort:
		arg1 = op_stack.pop();
		result = makeStackTemporary(TypeCodes.INT, op_stack.height() + 1);
		op_stack.push(result);
		ins = new SystemCallNonVoid(mindex, optype, result, 
					    new SimpleJITOperand[] { arg1 });
		break;
	    case DereferenceOps.setByte:
	    case DereferenceOps.setShort:
		arg2 = op_stack.pop();
		arg1 = op_stack.pop();
		ins = new SystemCallVoid(mindex, optype, 
					 new SimpleJITOperand[] { arg1, arg2 });
		break;
	    case DereferenceOps.setBlock:
		arg3 = op_stack.pop();
		arg2 = op_stack.pop();
		arg1 = op_stack.pop();
		ins = new SystemCallVoid(mindex, optype, 
					 new SimpleJITOperand[] { arg1, arg2, arg3 });
		break;
	    default:
		throw new OVMError
		    ("Unsupported invoke_system dereference op argument : " + optype);
	    }
	    break;
	}

	default:
	    throw new OVMError("Unsupported invoke_system argument : " + mindex);
	}

	currentBlock.append(ins);
    }

    public void visit(Instruction.GETFIELD i) {
	if (LOG_ENABLED) log(pc_ + " : GETFIELD");
	Selector.Field sel = i.getSelector(buf, cp);
	Descriptor.Field desc = sel.getDescriptor();
	TypeName typeName  = desc.getType();
	char tag = standardizeTag(typeName.getTypeTag());
	SimpleJITOperand obj = op_stack.pop();
	SimpleJITOperand result = makeStackTemporary(tag, op_stack.height() + 1);
	op_stack.pushWithTag(tag, result);
	currentBlock.append(new GetField(result, obj, sel, i.getCPIndex(buf)));
    }

    public void visit(Instruction.PUTFIELD i) {
	if (LOG_ENABLED) log(pc_ + " : PUTFIELD");
	Selector.Field sel = i.getSelector(buf, cp);
	Descriptor.Field desc = sel.getDescriptor();
	TypeName typeName  = desc.getType();
	char tag = standardizeTag(typeName.getTypeTag());
	SimpleJITOperand value = op_stack.popWithTag(tag);
	SimpleJITOperand obj = op_stack.pop();
	currentBlock.append(new PutField(obj, value, sel, i.getCPIndex(buf)));
    }

    public void visit(Instruction.PUTFIELD_WITH_BARRIER_REF i) {
	if (LOG_ENABLED) log(pc_ + " : PUTFIELD_WITH_BARRIER_REF");
	Selector.Field sel = i.getSelector(buf, cp);
	Descriptor.Field desc = sel.getDescriptor();
	TypeName typeName  = desc.getType();
	char tag = standardizeTag(typeName.getTypeTag());
	SimpleJITOperand value = op_stack.popWithTag(tag);
	SimpleJITOperand obj = op_stack.pop();
	currentBlock.append(new PutFieldWithBarrier(obj, value, sel, i.getCPIndex(buf)));
    }

    public void visit(Instruction.READ_BARRIER i) {
 	if (LOG_ENABLED) log(pc_ + " : READ_BARRIER");
	SimpleJITOperand val = op_stack.pop();
	currentBlock.append(new ReadBarrier(val));
    }

    public void visit(Instruction.GETSTATIC i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	Selector.Field sel = i.getSelector(buf, cp);
	Descriptor.Field desc = sel.getDescriptor();
	TypeName typeName  = desc.getType();
	char tag = standardizeTag(typeName.getTypeTag());
	SimpleJITOperand result = makeStackTemporary(tag, op_stack.height() + 1);
	op_stack.pushWithTag(tag, result);
	currentBlock.append(new GetStatic(result, sel, i.getCPIndex(buf)));
    }

    public void visit(Instruction.PUTSTATIC i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	Selector.Field sel = i.getSelector(buf, cp);
	Descriptor.Field desc = sel.getDescriptor();
	TypeName typeName  = desc.getType();
	char tag = standardizeTag(typeName.getTypeTag());
	SimpleJITOperand value = op_stack.popWithTag(tag);
	currentBlock.append(new PutStatic(value, sel, i.getCPIndex(buf)));
    }

    public void visit(Instruction.PUTSTATIC_WITH_BARRIER_REF i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
 	Selector.Field sel = i.getSelector(buf, cp);
 	Descriptor.Field desc = sel.getDescriptor();
 	TypeName typeName  = desc.getType();
 	char tag = standardizeTag(typeName.getTypeTag());
 	SimpleJITOperand value = op_stack.popWithTag(tag);
 	currentBlock.append(new PutStaticWithBarrier(value, sel, i.getCPIndex(buf)));
    }

    public void visit(Instruction.SINGLEANEWARRAY i) {
	if (LOG_ENABLED) log(pc_ + " : SINGLEANEWARRAY");
	TypeName.Array tn = i.getArrayName(buf, cp);
	char tag = standardizeTag(tn.getTypeTag());
	SimpleJITOperand dimension = op_stack.pop();
	SimpleJITOperand result = makeStackTemporary(tag, op_stack.height() + 1);
	op_stack.push(result);
	currentBlock.append(new ANewArray(result, dimension, tn, i.getCPIndex(buf)));
    }

    public void visit(Instruction.ANEWARRAY i) {
	if (LOG_ENABLED) log(pc_ + " : ANEWARRAY");
	TypeName.Array tn = i.getArrayName(buf, cp);
	char tag = standardizeTag(tn.getTypeTag());
	SimpleJITOperand dimension = op_stack.pop();
	SimpleJITOperand result = makeStackTemporary(tag, op_stack.height() + 1);
	op_stack.push(result);
	currentBlock.append(new ANewArray(result, dimension, tn, i.getCPIndex(buf)));
    }

    public void visit(Instruction.NEWARRAY i) {
	if (LOG_ENABLED) log(pc_ + " : NEWARRAY");
	TypeName.Array tn = i.getArrayName(buf);
	char tag = standardizeTag(tn.getTypeTag());
	SimpleJITOperand dimension = op_stack.pop();
	SimpleJITOperand result = makeStackTemporary(tag, op_stack.height() + 1);
	op_stack.push(result);
	currentBlock.append(new NewArray(result, dimension, tn));
    }

    public void visit(Instruction.MULTIANEWARRAY i) {
	if (LOG_ENABLED) log(pc_ + " : MULTIANEWARRAY");
	TypeName.Array tn = i.getClassName(buf, cp);
	char tag = standardizeTag(tn.getTypeTag());
	int dimensions = i.getDimensions(buf);
	SimpleJITOperand[] dimOp = new SimpleJITOperand[dimensions];
	for(int j = dimensions - 1; j >= 0; j--) {
	    dimOp[j] = op_stack.pop();
	}
	SimpleJITOperand result = makeStackTemporary(tag, op_stack.height() + 1);
	op_stack.push(result);
	currentBlock.append(new MultiANewArray(result, dimOp, tn, i.getCPIndex(buf)));
    }

    public void visit(Instruction.CHECKCAST i) {
	if (LOG_ENABLED) log(pc_ + " : CHECKCAST");
	TypeName.Compound tn = i.getResultTypeName(buf, cp);
	SimpleJITOperand input = op_stack.pop();
	SimpleJITOperand result = makeStackTemporary(TypeCodes.REFERENCE, op_stack.height() + 1);
	op_stack.push(result);
	currentBlock.append(new CheckCast(result, input, tn, i.getCPIndex(buf)));
    }

    public void visit(Instruction.INSTANCEOF i) {
	if (LOG_ENABLED) log(pc_ + " : INSTANCEOF");
	TypeName.Compound tn = i.getTypeName(buf, cp);
	SimpleJITOperand input = op_stack.pop();
	SimpleJITOperand result = makeStackTemporary(TypeCodes.INT, op_stack.height() + 1);
	op_stack.push(result);
	currentBlock.append(new InstanceOf(result, input, tn, i.getCPIndex(buf)));
    }

    public void visit(Instruction.NEW i) {
	if (LOG_ENABLED) log(pc_ + " : NEW");
	TypeName.Scalar tn = i.getClassName(buf, cp);
	SimpleJITOperand result = makeStackTemporary(TypeCodes.REFERENCE, op_stack.height() + 1);
	op_stack.push(result);
	currentBlock.append(new New(result, tn, i.getCPIndex(buf)));
    }

    public void visit(Instruction.RETURN i) {
	if (LOG_ENABLED) log(pc_ + " : RETURN");
	currentBlock.append(new Return());
    }

    public void visit(Instruction.IRETURN i) {
	if (LOG_ENABLED) log(pc_ + " : IRETURN");
	SimpleJITOperand v = op_stack.pop();
	currentBlock.append(new IReturn(v));
    }

    public void visit(Instruction.LRETURN i) {
	if (LOG_ENABLED) log(pc_ + " : LRETURN");
	SimpleJITOperand v = op_stack.popWide();
	currentBlock.append(new LReturn(v));
    }

    public void visit(Instruction.FRETURN i) {
	if (LOG_ENABLED) log(pc_ + " : FRETURN");
	SimpleJITOperand v = op_stack.pop();
	currentBlock.append(new FReturn(v));
    }

    public void visit(Instruction.DRETURN i) {
	if (LOG_ENABLED) log(pc_ + " : DRETURN");
	SimpleJITOperand v = op_stack.popWide();
	currentBlock.append(new DReturn(v));
    }

    public void visit(Instruction.ARETURN i) {
	if (LOG_ENABLED) log(pc_ + " : ARETURN");
	SimpleJITOperand v = op_stack.pop();
	currentBlock.append(new AReturn(v));
    }

    public void visit(Instruction.IINC i) {
	if (LOG_ENABLED) log(pc_ + " : IINC");
	int index = i.getLocalVariableOffset(buf);
	int delta = i.getValue(buf);
	SimpleJITOperand op = makeLocalTemporary(TypeCodes.INT, index);
	SimpleJITIntOperand d = new SimpleJITIntOperand(delta);
	currentBlock.append(new BinaryOp(Operators.INT_ADD, op, op, d));
    }

    public void visit(Instruction.WIDE_IINC i) {
	if (LOG_ENABLED) log(pc_ + " : WIDE_IINC");
	int index = i.getLocalVariableOffset(buf);
	int delta = i.getValue(buf);
	SimpleJITOperand op = makeLocalTemporary(TypeCodes.INT, index);
	SimpleJITIntOperand d = new SimpleJITIntOperand(delta);
	currentBlock.append(new BinaryOp(Operators.INT_ADD, op, op, d));
    }

    public void visit(Instruction.MONITORENTER i) {
	if (LOG_ENABLED) log(pc_ + " : MONITORENTER");
	SimpleJITOperand obj = op_stack.pop();
	currentBlock.append(new MonitorEnter(obj));
    }

    public void visit(Instruction.MONITOREXIT i) {
	if (LOG_ENABLED) log(pc_ + " : MONITOREXIT");
	SimpleJITOperand obj = op_stack.pop();
	currentBlock.append(new MonitorExit(obj));
    }

    public void visit(Instruction.ATHROW i) {
	if (LOG_ENABLED) log(pc_ + " : ATHROW");
	SimpleJITOperand obj = op_stack.pop();
	currentBlock.append(new AThrow(obj));
    }

    public void visit(Instruction.POLLCHECK i) {
	if (LOG_ENABLED) log(pc_ + " : POLLCHECK");
	currentBlock.append(new PollCheck());
    }

    public void visit(Instruction.DUP i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	op_stack.dup();
	currentBlock.appendNothing();
    }

    public void visit(Instruction.DUP_X1 i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	op_stack.dup_x1();
	currentBlock.appendNothing();
    }

    public void visit(Instruction.DUP_X2 i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	op_stack.dup_x2();
	currentBlock.appendNothing();
    }

    public void visit(Instruction.DUP2 i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	op_stack.dup2();
	currentBlock.appendNothing();
    }

    public void visit(Instruction.DUP2_X1 i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	op_stack.dup2_x1();
	currentBlock.appendNothing();
    }

    public void visit(Instruction.DUP2_X2 i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	op_stack.dup2_x2();
	currentBlock.appendNothing();
    }

    public void visit(Instruction.POP i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	op_stack.pop();
	currentBlock.appendNothing();
    }

    public void visit(Instruction.POP2 i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	op_stack.pop2();
	currentBlock.appendNothing();
    }

    public void visit(Instruction.SWAP i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	op_stack.swap();
	currentBlock.appendNothing();
    }

    public void visit(Instruction.ROLL i) {
	if (LOG_ENABLED) log(pc_ + " : ROLL");
	int span = i.getSpan(buf);
	int count = i.getCount(buf);
	SimpleJITOperand[] tmp = new SimpleJITOperand[span];
	for (int j = span - 1; j >= 0; j--) {
	    tmp[j] = op_stack.pop();
	}
	for (int j = 0; j < span; j++) {
	    op_stack.push(tmp[(j + span - count) % span]);
	}
	currentBlock.appendNothing();
    }



    public void visit(Instruction.IADD i) {
	if (LOG_ENABLED) log(pc_ + " : IADD");
	visitBinaryOp(TypeCodes.INT, Operators.INT_ADD);
    }

    public void visit(Instruction.ISUB i) {
	if (LOG_ENABLED) log(pc_ + " : ISUB");
	visitBinaryOp(TypeCodes.INT, Operators.INT_SUB);
    }

    public void visit(Instruction.IMUL i) {
	if (LOG_ENABLED) log(pc_ + " : IMUL");
	visitBinaryOp(TypeCodes.INT, Operators.INT_MUL);
    }

    public void visit(Instruction.IDIV i) {
	if (LOG_ENABLED) log(pc_ + " : IDIV");
	visitBinaryOp(TypeCodes.INT, Operators.INT_DIV);
    }

    public void visit(Instruction.IREM i) {
	if (LOG_ENABLED) log(pc_ + " : IREM");
	visitBinaryOp(TypeCodes.INT, Operators.INT_REM);
    }

    public void visit(Instruction.LADD i) {
	if (LOG_ENABLED) log(pc_ + " : LADD");
	visitBinaryOp(TypeCodes.LONG, Operators.LONG_ADD);
    }

    public void visit(Instruction.LSUB i) {
	if (LOG_ENABLED) log(pc_ + " : LSUB");
	visitBinaryOp(TypeCodes.LONG, Operators.LONG_SUB);
    }

    public void visit(Instruction.LMUL i) {
	if (LOG_ENABLED) log(pc_ + " : LMUL");
	visitBinaryOp(TypeCodes.LONG, Operators.LONG_MUL);
    }

    public void visit(Instruction.LDIV i) {
	if (LOG_ENABLED) log(pc_ + " : LDIV");
	visitBinaryOp(TypeCodes.LONG, Operators.LONG_DIV);
    }

    public void visit(Instruction.LREM i) {
	if (LOG_ENABLED) log(pc_ + " : LREM");
	visitBinaryOp(TypeCodes.LONG, Operators.LONG_REM);
    }

    public void visit(Instruction.FADD i) {
	if (LOG_ENABLED) log(pc_ + " : FADD");
	visitBinaryOp(TypeCodes.FLOAT, Operators.FLOAT_ADD);
    }

    public void visit(Instruction.FSUB i) {
	if (LOG_ENABLED) log(pc_ + " : FSUB");
	visitBinaryOp(TypeCodes.FLOAT, Operators.FLOAT_SUB);
    }

    public void visit(Instruction.FMUL i) {
	if (LOG_ENABLED) log(pc_ + " : FMUL");
	visitBinaryOp(TypeCodes.FLOAT, Operators.FLOAT_MUL);
    }

    public void visit(Instruction.FDIV i) {
	if (LOG_ENABLED) log(pc_ + " : FDIV");
	visitBinaryOp(TypeCodes.FLOAT, Operators.FLOAT_DIV);
    }

    public void visit(Instruction.FREM i) {
	if (LOG_ENABLED) log(pc_ + " : FREM");
	visitBinaryOp(TypeCodes.FLOAT, Operators.FLOAT_REM);
    }

    public void visit(Instruction.DADD i) {
	if (LOG_ENABLED) log(pc_ + " : DADD");
	visitBinaryOp(TypeCodes.DOUBLE, Operators.DOUBLE_ADD);
    }

    public void visit(Instruction.DSUB i) {
	if (LOG_ENABLED) log(pc_ + " : DSUB");
	visitBinaryOp(TypeCodes.DOUBLE, Operators.DOUBLE_SUB);
    }

    public void visit(Instruction.DMUL i) {
	if (LOG_ENABLED) log(pc_ + " : DMUL");
	visitBinaryOp(TypeCodes.DOUBLE, Operators.DOUBLE_MUL);
    }

    public void visit(Instruction.DDIV i) {
	if (LOG_ENABLED) log(pc_ + " : DDIV");
	visitBinaryOp(TypeCodes.DOUBLE, Operators.DOUBLE_DIV);
    }

    public void visit(Instruction.DREM i) {
	if (LOG_ENABLED) log(pc_ + " : DREM");
	visitBinaryOp(TypeCodes.DOUBLE, Operators.DOUBLE_REM);
    }

    public void visit(Instruction.ISHL i) {
	if (LOG_ENABLED) log(pc_ + " : ISHL");
	visitBinaryOp(TypeCodes.INT, Operators.INT_SHL);
    }

    public void visit(Instruction.ISHR i) {
	if (LOG_ENABLED) log(pc_ + " : ISHR");
	visitBinaryOp(TypeCodes.INT, Operators.INT_SHR);
    }

    public void visit(Instruction.IUSHR i) {
	if (LOG_ENABLED) log(pc_ + " : IUSHR");
	visitBinaryOp(TypeCodes.INT, Operators.INT_USHR);
    }

    public void visit(Instruction.LSHL i) {
	if (LOG_ENABLED) log(pc_ + " : LSHL");
	visitBinaryOp(TypeCodes.LONG, TypeCodes.LONG, TypeCodes.INT, Operators.LONG_SHL);
    }

    public void visit(Instruction.LSHR i) {
	if (LOG_ENABLED) log(pc_ + " : LSHR");
	visitBinaryOp(TypeCodes.LONG, TypeCodes.LONG, TypeCodes.INT, Operators.LONG_SHR);
    }

    public void visit(Instruction.LUSHR i) {
	if (LOG_ENABLED) log(pc_ + " : LUSHR");
	visitBinaryOp(TypeCodes.LONG, TypeCodes.LONG, TypeCodes.INT, Operators.LONG_USHR);
    }

    public void visit(Instruction.INEG i) {
	if (LOG_ENABLED) log(pc_ + " : INEG");
	visitUnaryOp(TypeCodes.INT, TypeCodes.INT, Operators.INT_NEG);
    }

    public void visit(Instruction.IAND i) {
	if (LOG_ENABLED) log(pc_ + " : IAND");
	visitBinaryOp(TypeCodes.INT, Operators.INT_AND);
    }

    public void visit(Instruction.IOR i) {
	if (LOG_ENABLED) log(pc_ + " : IOR");
	visitBinaryOp(TypeCodes.INT, Operators.INT_OR);
    }

    public void visit(Instruction.IXOR i) {
	if (LOG_ENABLED) log(pc_ + " : IXOR");
	visitBinaryOp(TypeCodes.INT, Operators.INT_XOR);
    }

    public void visit(Instruction.LNEG i) {
	if (LOG_ENABLED) log(pc_ + " : LNEG");
	visitUnaryOp(TypeCodes.LONG, TypeCodes.LONG, Operators.LONG_NEG);
    }

    public void visit(Instruction.LAND i) {
	if (LOG_ENABLED) log(pc_ + " : LAND");
	visitBinaryOp(TypeCodes.LONG, Operators.LONG_AND);
    }

    public void visit(Instruction.FNEG i) {
	if (LOG_ENABLED) log(pc_ + " : FNEG");
	visitUnaryOp(TypeCodes.FLOAT, TypeCodes.FLOAT, Operators.FLOAT_NEG); 
    }

    public void visit(Instruction.DNEG i) {
	if (LOG_ENABLED) log(pc_ + " : DNEG");
	visitUnaryOp(TypeCodes.DOUBLE, TypeCodes.DOUBLE, Operators.DOUBLE_NEG);
    }

    public void visit(Instruction.LOR i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitBinaryOp(TypeCodes.LONG, Operators.LONG_OR);
    }

    public void visit(Instruction.LXOR i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitBinaryOp(TypeCodes.LONG, Operators.LONG_XOR);
    }

    public void visit(Instruction.I2L i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.LONG, TypeCodes.INT, Operators.INT2LONG);
    }

    public void visit(Instruction.I2F i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.FLOAT, TypeCodes.INT, Operators.INT2FLOAT);
    }

    public void visit(Instruction.I2D i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.DOUBLE, TypeCodes.INT, Operators.INT2DOUBLE);
    }

    public void visit(Instruction.L2I i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.INT, TypeCodes.LONG, Operators.LONG2INT);
    }

    public void visit(Instruction.L2F i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.FLOAT, TypeCodes.LONG, Operators.LONG2FLOAT);
    }

    public void visit(Instruction.L2D i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.DOUBLE, TypeCodes.LONG, Operators.LONG2DOUBLE);
    }

    public void visit(Instruction.F2I i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.INT, TypeCodes.FLOAT, Operators.FLOAT2INT);
    }

    public void visit(Instruction.F2L i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.LONG, TypeCodes.FLOAT, Operators.FLOAT2LONG);
    }

    public void visit(Instruction.F2D i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.DOUBLE, TypeCodes.FLOAT, Operators.FLOAT2DOUBLE);
    }

    public void visit(Instruction.D2I i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.INT, TypeCodes.DOUBLE, Operators.DOUBLE2INT);
    }

    public void visit(Instruction.D2L i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.LONG, TypeCodes.DOUBLE, Operators.DOUBLE2LONG);
    }

    public void visit(Instruction.D2F i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.FLOAT, TypeCodes.DOUBLE, Operators.DOUBLE2FLOAT);
    }

    public void visit(Instruction.I2C i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.INT, TypeCodes.INT, Operators.INT2CHAR);
    }

    public void visit(Instruction.I2S i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.INT, TypeCodes.INT, Operators.INT2SHORT);
    }

    public void visit(Instruction.I2B i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitUnaryOp(TypeCodes.INT, TypeCodes.INT, Operators.INT2BYTE);
    }

    public void visit(Instruction.LCMP i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitBinaryOp(TypeCodes.INT, TypeCodes.LONG, TypeCodes.LONG, Operators.LONG_CMP);
    }

    public void visit(Instruction.FCMPG i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitBinaryOp(TypeCodes.INT, TypeCodes.FLOAT, TypeCodes.FLOAT, Operators.FLOAT_CMPG);
    }

    public void visit(Instruction.FCMPL i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitBinaryOp(TypeCodes.INT, TypeCodes.FLOAT, TypeCodes.FLOAT, Operators.FLOAT_CMPL);
    }

    public void visit(Instruction.DCMPG i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitBinaryOp(TypeCodes.INT, TypeCodes.DOUBLE, TypeCodes.DOUBLE, Operators.DOUBLE_CMPG);
    }

    public void visit(Instruction.DCMPL i) {
	if (LOG_ENABLED) log(pc_ + " : " + i);
	visitBinaryOp(TypeCodes.INT, TypeCodes.DOUBLE, TypeCodes.DOUBLE, Operators.DOUBLE_CMPL);
    }

    // Quick instructions. They are not supposed to be seen by this
    // visitor
    public void visit(Instruction.GETFIELD_QUICK i) {
//	throw new NeedsFiatError("SimpleJITBC2IR found a GETFIELD_QUICK");
	if (LOG_ENABLED) log(pc_ + " : GETFIELD_QUICK");
	SimpleJITOperand obj = op_stack.pop();
	SimpleJITOperand result = 
	    makeStackTemporary(TypeCodes.INT, op_stack.height() + 1);
	op_stack.push(result);
	currentBlock.append(new GetField(result, obj, null, -1));
    }
    public void visit(Instruction.REF_GETFIELD_QUICK i) {
//	throw new NeedsFiatError("SimpleJITBC2IR found a REF_GETFIELD_QUICK");
	if (LOG_ENABLED) log(pc_ + " : REF_GETFIELD_QUICK");
	SimpleJITOperand obj = op_stack.pop();
	SimpleJITOperand result = 
	    makeStackTemporary(TypeCodes.REFERENCE, op_stack.height() + 1);
	op_stack.push(result);
	currentBlock.append(new GetField(result, obj, null, -1));
    }
    public void visit(Instruction.GETFIELD2_QUICK i) {
//	throw new NeedsFiatError("SimpleJITBC2IR found a GETFIELD2_QUICK");
	if (LOG_ENABLED) log(pc_ + " : GETFIELD2_QUICK");
	SimpleJITOperand obj = op_stack.pop();
	SimpleJITOperand result = 
	    makeStackTemporary(TypeCodes.LONG, op_stack.height() + 1);
	op_stack.pushWide(result);
	currentBlock.append(new GetField(result, obj, null, -1));
    }
    public void visit(Instruction.PUTFIELD_QUICK i) {
//	throw new NeedsFiatError("SimpleJITBC2IR found a PUTFIELD_QUICK");
	if (LOG_ENABLED) log(pc_ + " : PUTFIELD_QUICK");
	SimpleJITOperand value = op_stack.pop();
	SimpleJITOperand obj = op_stack.pop();
	currentBlock.append(new PutField(obj, value, null, -1));
    }
    public void visit(Instruction.PUTFIELD2_QUICK i) {
//	throw new NeedsFiatError("SimpleJITBC2IR found a PUTFIELD2_QUICK");
	if (LOG_ENABLED) log(pc_ + " : PUTFIELD2_QUICK");
	SimpleJITOperand value = op_stack.popWide();
	SimpleJITOperand obj = op_stack.pop();
	currentBlock.append(new PutField(obj, value, null, -1));
    }
    public void visit(Instruction.INVOKESUPER_QUICK i) {
//	throw new NeedsFiatError("SimpleJITBC2IR found an INVOKESUPER_QUICK");
	if (LOG_ENABLED) log(pc_ + " : INVOKESUPER_QUICK");
	visitInvocationQuick(i, Operators.INVOKESPECIAL);
    }
    public void visit(Instruction.INVOKENONVIRTUAL_QUICK i) {
//	throw new NeedsFiatError("SimpleJITBC2IR found an INVOKENONVIRTUAL_QUICK");
	if (LOG_ENABLED) log(pc_ + " : INVOKENONVIRTUAL_QUICK");
	visitInvocationQuick(i, Operators.INVOKESTATIC);
    }
    public void visit(Instruction.INVOKENONVIRTUAL2_QUICK i) {
//	throw new NeedsFiatError("SimpleJITBC2IR found an INVOKENONVIRTUAL2_QUICK");
	if (LOG_ENABLED) log(pc_ + " : INVOKENONVIRTUAL2_QUICK");
	visitInvocationQuick(i, Operators.INVOKESPECIAL);
    }
    public void visit(Instruction.INVOKEINTERFACE_QUICK i) {
//	throw new NeedsFiatError("SimpleJITBC2IR found an INVOKEINTERFACE_QUICK");
	if (LOG_ENABLED) log(pc_ + " : INVOKEINTERFACE_QUICK");
	visitInvocationQuick(i, Operators.INVOKEINTERFACE);
    }
    public void visit(Instruction.INVOKEVIRTUAL_QUICK i) {
//	throw new NeedsFiatError("SimpleJITBC2IR found an INVOKEVIRTUAL_QUICK");
	if (LOG_ENABLED) log(pc_ + " : INVOKEVIRTUAL_QUICK");
	visitInvocationQuick(i, Operators.INVOKEVIRTUAL);
    }
    public void visit(Instruction.NEW_QUICK i) {
	throw new NeedsFiatError("SimpleJITBC2IR found a NEW_QUICK");
    }
    public void visit(Instruction.ANEWARRAY_QUICK i) {
	throw new NeedsFiatError("SimpleJITBC2IR found an ANEWARRAY_QUICK");
    }
    public void visit(Instruction.MULTIANEWARRAY_QUICK i) {
	throw new NeedsFiatError("SimpleJITBC2IR found a MULTIANEWARRAY_QUICK");
    }
    public void visit(Instruction.INSTANCEOF_QUICK i) {
	throw new NeedsFiatError("SimpleJITBC2IR found an INSTANCEOF_QUICK"); 
    }
    public void visit(Instruction.CHECKCAST_QUICK i) {
	throw new NeedsFiatError("SimpleJITBC2IR found a CHECKCAST_QUICK");
    }
}

