package s3.services.simplejit.bytecode;

import ovm.core.OVMBase;
import ovm.core.repository.Selector;
import ovm.core.repository.TypeName;
import ovm.core.repository.JavaNames;
import ovm.core.domain.Blueprint;
import ovm.core.domain.Domain;
import ovm.core.domain.Field;
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.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 org.ovmj.util.Runabout;
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.SimpleJITValue.SimpleJITArray;
import s3.services.simplejit.bytecode.SimpleJITValue.SimpleJITDouble;
import s3.services.simplejit.bytecode.SimpleJITValue.SimpleJITFloat;
import s3.services.simplejit.bytecode.SimpleJITValue.SimpleJITInt;
import s3.services.simplejit.bytecode.SimpleJITValue.SimpleJITJumpTarget;
import s3.services.simplejit.bytecode.SimpleJITValue.SimpleJITLong;
import s3.services.simplejit.bytecode.SimpleJITValue.SimpleJITReference;
import s3.services.simplejit.ir.SimpleJITBasicBlock;

/**
 * An abstract interpreter for SimpleJIT.
 *
 * This class is used to compute for SimpleJIT abstract type
 * information needed for generating GC maps and optimizations, using
 * our verifier infrastructure.
 * 
 * @author Hiroshi Yamauchi
 */
public class SimpleJITAbstractInterpreter 
    extends VerificationInterpreter 
    implements JVMConstants, JVMConstants.InvokeSystemArguments {

    public static class StateAndBlockMap {
	public State state;
	public HTint2Object entryPC2Block;
	private StateAndBlockMap(State state,
				 HTint2Object entryPC2Block) {
	    this.state = state;
	    this.entryPC2Block = entryPC2Block;
	}
    }

    public static class Driver {

	public Driver() {
	}

	public final S3ByteCode splitLocal
	    (S3ByteCode bc,
	     State state,
	     CompilerVMInterface compilerVMInterface) {
	    SimpleJITJSRProblemSolver solver =
		new SimpleJITJSRProblemSolver
		(bc,
		 state,
		 new SimpleJITEphemeralTypeMap
		 (compilerVMInterface.getTargetDomain()));
	    solver.run();
	    S3ByteCode rewritten = solver.getRewrittenByteCode();
	    return rewritten;
	}

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

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

	    StateAndBlockMap stateNBlocks = 
		runAbstractInterpreter(method, compilerVMInterface);

	    return stateNBlocks;
	}

	protected StateAndBlockMap runAbstractInterpreter
	    (S3Method method,
	     CompilerVMInterface compilerVMInterface) {

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

	    SimpleJITAbstractInterpreter ai = 
		new SimpleJITAbstractInterpreter
		(compilerVMInterface,
		 method, 
		 SimpleJITValue.bogusFactory);
	
	    SimpleJITFixpointIterator it = 
		new SimpleJITFixpointIterator
		(SimpleJITValue.bogusFactory,
		 ai, 
		 method,
		 InstructionBuffer
		 .wrap(method.getCode(S3ByteCode.KIND)));
	
	    Runabout[] visitors = new Runabout[] { 
		ai, it.getPostVisitor(),
	    };

	    try {
		for(Instruction i = it.next(); i != null; i = it.next())
		    for(int j = 0; j < visitors.length; j++)
			visitors[j].visitAppropriate(i);
		State state = it.getState();
		ai.setBlockExitPCs();
		HTint2Object blockmap = ai.getBlockMap();

		it.printStateLog();
		ai.printAllBlocks();
		ai.printBCMarking();

		// 	    // split locals
		//  	    SimpleJITJSRProblemSolver solver =
		//  		new SimpleJITJSRProblemSolver
		//  		((S3ByteCode)method.getCode(S3ByteCode.KIND), 
		//  		 state,
		// 		 new SimpleJITEphemeralTypeMap
		// 		 (compilerVMInterface.getTargetDomain(),
		// 		  compilerVMInterface.getRepository()));
		//  	    solver.run();
		// 	    S3ByteCode rewritten = solver.getRewrittenByteCode();
		// 	    if (rewritten != null)
		//  	        method.addCode(rewritten);
		//  	    ai.addLogByDataFlowAnalyzer(solver.getResultsAsString());
		// 	    it.printStateLog();

		return new StateAndBlockMap(state, blockmap);
	    } catch(SimpleJITAbstractInterpreter.UnwrittenCodeError e) {
		System.err.println(e.toString());
		System.err.println("CURRENT METHOD = " + sig);
		e.printStackTrace();
		System.exit(1);
	    } catch(SimpleJITAbstractInterpreter.TypeError e) {
		ai.printLogToStdErr();
		System.err.println(e.toString());
		System.err.println("CURRENT METHOD = " + sig);
		e.printStackTrace();
		System.exit(1);
	    } catch(SimpleJITAbstractInterpreter.NeedsFiatError e) {
		ai.printLogToStdErr();
		System.err.println(e.toString());
		System.err.println("CURRENT METHOD = " + sig);
		e.printStackTrace();
		System.exit(1);
	    } catch(ovm.core.domain.LinkageException.Runtime e) {
		ai.printLogToStdErr();
		System.err.println(e.toString());
		System.err.println("CURRENT METHOD = " + sig);
		e.printStackTrace();
	    } catch(Throwable t) {
		ai.printLogToStdErr();
		System.err.println(t.toString());
		System.err.println("CURRENT METHOD = " + sig);
		t.printStackTrace();
		System.exit(1);
	    }
	    return null;
	}
    }

    // 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;

    /** If this option is enabled, a type error will be reported as an
     * expection. */
    private static final boolean TYPE_ASSERTION_ENABLED = true;

    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 SimpleJITEphemeralTypeMap ephemeralTypeMap;
    protected S3ByteCode bytecode;

    public static final byte BC_UNTOUCHED = 0;
    public static final byte BC_BBSTART   = 1;
    public static final byte BC_INBLOCK   = 2;
    protected byte[] bcmarking;

    HTint2Object entryPC2Block;
    protected SimpleJITBasicBlock currentBlock;

    public SimpleJITAbstractInterpreter
	(CompilerVMInterface compilerVMInterface,
	 S3Method m, 
	 AbstractValue.Factory avf) {
	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("SJAI", System.err)
	    : new SimpleJITLogger("SJAI", LOG_SIZE);
	stringBP = 
	    blueprintFor(targetDomain,
			 RepositoryUtils.makeTypeName("java/lang", "String"));
	contextBP = 
	    blueprintFor
	    (executiveDomain, 
	     RepositoryUtils.makeTypeName("ovm/core/execution", "Context"));
	ephemeralTypeMap =
	    new SimpleJITEphemeralTypeMap(targetDomain);
	entryPC2Block = new HTint2Object();
	currentBlock = new SimpleJITBasicBlock(0);
	entryPC2Block.put(0, currentBlock);
	bcmarking = new byte[bytecode.getBytes().length];
	bcmarking[0] = BC_BBSTART;
    }

    public void markAsBlockStart(int pc) {
	bcmarking[pc] = BC_BBSTART;
    }
    public void markAsInBlock(int pc) {
	bcmarking[pc] = BC_INBLOCK;
    }
    public boolean isBlockStart(int pc) {
	return bcmarking[pc] == BC_BBSTART;
    }
    public boolean isInBlock(int pc) {
	return bcmarking[pc] == BC_INBLOCK;
    }
    public boolean isUntouched(int pc) {
	return bcmarking[pc] == BC_UNTOUCHED;
    }
    public SimpleJITBasicBlock searchForBlockBackward(int pc) {
	for(int i = pc; i >= 0; i--) {
	    if (bcmarking[i] == BC_BBSTART)
		return (SimpleJITBasicBlock)entryPC2Block.get(i);
	}
	throw new Error();
    }

    private void setBlockExitPCs() {
	SimpleJITBasicBlock curBlock = null;
	for(int pc = 0; pc < bcmarking.length; pc++) {
	    if (bcmarking[pc] == BC_BBSTART) {
		if (curBlock != null) {
		    curBlock.setExitPC(pc);
		}
		curBlock = (SimpleJITBasicBlock)
		    entryPC2Block.get(pc);
	    }
	}
	curBlock.setExitPC(bcmarking.length);
    }

    public HTint2Object getBlockMap() {
	return entryPC2Block;
    }

    public SimpleJITBasicBlock getCurrentBlock() {
	return currentBlock;
    }
    public void setCurrentBlockByPC(int pc) {
	Object b = entryPC2Block.get(pc);
	if (b != null)
	    currentBlock = (SimpleJITBasicBlock)b;
    }
    public SimpleJITBasicBlock getBlockAtEntryPC(int pc) {
	SimpleJITBasicBlock b = 
	    (SimpleJITBasicBlock)entryPC2Block.get(pc);
	return b;
    }
    public SimpleJITBasicBlock getOrCreateBlockAtEntryPC(int pc) {
	SimpleJITBasicBlock b = 
	    (SimpleJITBasicBlock)entryPC2Block.get(pc);
	if (b == null) {
	    b = new SimpleJITBasicBlock(pc);
	    entryPC2Block.put(pc, b);
	}
	return b;
    }
    public SimpleJITBasicBlock getOrCreateHandlerBlockAtEntryPC(int pc, 
								TypeName.Scalar ex) {
	SimpleJITBasicBlock b = 
	    (SimpleJITBasicBlock)entryPC2Block.get(pc);
	if (b == null) {
	    b = new SimpleJITBasicBlock(pc);
	    b.markAsHandlerBlock(ex);
	    entryPC2Block.put(pc, b);
	}
	return b;
    }

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

    public void printBCMarking() {
	log("BCMarking:");
	int BClength = bytecode.getBytes().length;
	for(int i = 0; i < BClength; i++) {
	    String s = null;
	    switch(bcmarking[i]) {
	    case BC_UNTOUCHED:
		s = "X";
		break;
	    case BC_BBSTART:
		s = "B";
		break;
	    case BC_INBLOCK:
		s = "|";
		break;
	    default:
		throw new Error();
	    }
	    log(i + "\t: " + s);
	}
    }

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

    public Domain getTargetDomain() {
	return targetDomain;
    }

    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.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.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.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.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.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;
    }

    protected void typeAssert(boolean condition) {
	if (TYPE_ASSERTION_ENABLED && ! condition)
	    throw new TypeError();
    }

    protected SimpleJITJumpTarget typeCheckJumpTarget(AbstractValue v) {
	typeAssert(v instanceof SimpleJITJumpTarget);
	return (SimpleJITJumpTarget)v;
    }
    protected SimpleJITReference typeCheckReference(AbstractValue v) {
	typeAssert(v instanceof SimpleJITReference);
	return (SimpleJITReference)v;
    }
    protected SimpleJITArray typeCheckArray(AbstractValue v) {
	typeAssert(v instanceof SimpleJITArray);
	return (SimpleJITArray)v;
    }
    protected SimpleJITInt typeCheckInt(AbstractValue v) {
	SimpleJITValue v1 = ephemeralTypeMap.map((SimpleJITValue)v);
	typeAssert(v1 instanceof SimpleJITInt);
	return (SimpleJITInt)v1;
    }
    protected SimpleJITLong typeCheckLong(AbstractValue v) {
	typeAssert(v instanceof SimpleJITLong);
	return (SimpleJITLong)v;
    }
    protected SimpleJITFloat typeCheckFloat(AbstractValue v) {
	typeAssert(v instanceof SimpleJITFloat);
	return (SimpleJITFloat)v;
    }
    protected SimpleJITDouble typeCheckDouble(AbstractValue v) {
	typeAssert(v instanceof SimpleJITDouble);
	return (SimpleJITDouble)v;
    }

    protected SimpleJITValue popAndTypeCheck(TypeName typeName, 
					     Frame frame) {
	/* A special case for VM_Word/VM_Address. When we eventually
	 * see no trace of VM_Word/VM_Address in our rewritten IR in
	 * the future, this special case wouldn't be necessary.
	 * -Hiroshi
	 */
	if (typeName == JavaNames.ovm_core_services_memory_VM_Word
	    || typeName == JavaNames.ovm_core_services_memory_VM_Address) {
	    AbstractValue v = frame_.pop();
	    try {
		return typeCheckReference(v);
	    } catch (TypeError te0) {
		try {
		    return typeCheckInt(v);
		} catch (TypeError te1) {
		    throw te1;
		}
	    }
	} else {
	    char tag = typeName.getTypeTag();
	    switch(tag) {
	    case TypeCodes.BYTE:
	    case TypeCodes.BOOLEAN:
	    case TypeCodes.CHAR:
	    case TypeCodes.SHORT:
	    case TypeCodes.INT:
		return typeCheckInt(frame.pop());
	    case TypeCodes.FLOAT:
		return typeCheckFloat(frame.pop());
	    case TypeCodes.LONG:
		return typeCheckLong(frame.popWide());
	    case TypeCodes.DOUBLE:
		return typeCheckDouble(frame.popWide());
	    case TypeCodes.OBJECT:
		return typeCheckReference(frame.pop());
	    case TypeCodes.ARRAY:
		AbstractValue v = frame.pop();
		if (v instanceof SimpleJITArray)
		    return typeCheckArray(v);
		else if (v instanceof SimpleJITReference
			 && ((SimpleJITReference)v).isNullOrUnknownType())
		    return typeCheckReference(v);
		else
		    throw new OVMError("Unexpected type : " + v);
		//return typeCheckArray(frame.pop());
	    default:
		throw new OVMError("Unexpected type tag : " + tag);
	    }
	}
    }

    void printEntryPC2Block() {
    }

    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;
	}
    }

    public SimpleJITValue type2SimpleJITValue(Domain domain, 
					      Type t) {
	char tag = t.getUnrefinedName().getTypeTag();
	Blueprint bp;
	switch(tag) {
	case TypeCodes.BYTE:
	case TypeCodes.BOOLEAN:
	case TypeCodes.CHAR:
	case TypeCodes.SHORT:
	case TypeCodes.INT:
	    return new SimpleJITInt();
	case TypeCodes.LONG:
	    return new SimpleJITLong();
	case TypeCodes.FLOAT:
	    return new SimpleJITFloat();
	case TypeCodes.DOUBLE:
	    return new SimpleJITDouble();
	case TypeCodes.OBJECT:
	    bp = blueprintFor(domain, t.asCompound());
	    return new SimpleJITReference(bp);
	case TypeCodes.ARRAY:
	    bp = blueprintFor(domain, t.asCompound());
	    return new SimpleJITArray(bp.asArray());
	default:
	    throw new OVMError("Unexpected type tag : " + tag);
	}
    }

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

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

    void addLogByDataFlowAnalyzer(String message) {
	log(message);
    }
    void addLogByFixpointIterator(String message) {
	log(message);
    }

    public void visit(Instruction i) {
	if (LOG_ENABLED) log(pc_ + " : No visit method for " + i.toString());
	throw new OVMError.UnsupportedOperation();
    }

    public void visit(Instruction.NOP i) {
	if (LOG_ENABLED) log(pc_ + " : NOP");
    }

    public void visit(Instruction.AFIAT i) {
	if (LOG_ENABLED) log(pc_ + " : AFIAT " + i);
	frame_.pop();
	TypeName.Compound tn = i.getResultTypeName(buf, cp).asCompound();
	Blueprint t = blueprintFor(targetDomain, tn);
	if (tn.isArray())
	    frame_.push(new SimpleJITArray(t.asArray()));
	else
	    frame_.push(new SimpleJITReference(t));
    }

    public void visit(Instruction.IFIAT i) {
	if (LOG_ENABLED) log(pc_ + " : IFIAT");
	frame_.pop();
	frame_.push(new SimpleJITInt());
    }
    public void visit(Instruction.FFIAT i) {
	if (LOG_ENABLED) log(pc_ + " : FFIAT");
	frame_.pop();
	frame_.push(new SimpleJITFloat());
    }
    public void visit(Instruction.LFIAT i) {
	if (LOG_ENABLED) log(pc_ + " : LFIAT");
	frame_.popWide();
	frame_.pushWide(new SimpleJITLong());
    }
    public void visit(Instruction.DFIAT i) {
	if (LOG_ENABLED) log(pc_ + " : DFIAT");
	frame_.popWide();
	frame_.pushWide(new SimpleJITDouble());
    }

    public void visit(Instruction.ACONST_NULL i) {
	if (LOG_ENABLED) log(pc_ + " : ACONST_NULL");
	frame_.push(new SimpleJITReference().declareNull());
    }

    public void visit(Instruction.IConstantLoad i) {
	if (LOG_ENABLED) log(pc_ + " : IConstantLoad " + i);
	frame_.push(new SimpleJITInt(i.getValue(buf)));
    }

    public void visit(Instruction.FConstantLoad i) {
	if (LOG_ENABLED) log(pc_ + " : FConstantLoad " + i);
	frame_.push(new SimpleJITFloat(i.getFValue(buf)));
    }

    public void visit(Instruction.LConstantLoad i) {
	if (LOG_ENABLED) log(pc_ + " : LConstantLoad " + i);
	frame_.pushWide(new SimpleJITLong(i.getLValue(buf)));
    }

    public void visit(Instruction.DConstantLoad i) {
	if (LOG_ENABLED) log(pc_ + " : DConstantLoad " + i);
	frame_.pushWide(new SimpleJITDouble(i.getDValue(buf)));
    }

    public void visit(Instruction.ConstantPoolLoad i) {
	if (LOG_ENABLED) log(pc_ + " : ConstantPoolLoad " + i);
	int cpindex = i.getCPIndex(buf);
	switch(cp.getTagAt(cpindex)) {
	case CONSTANT_Integer:
	    frame_.push(new SimpleJITInt(cp.getValueAt(cpindex)));
	    break;
	case CONSTANT_Float:
	    frame_.push
		(new SimpleJITFloat
		 (Float.intBitsToFloat(cp.getValueAt(cpindex))));
	    break;
	case CONSTANT_Long:
	    frame_.pushWide
		(new SimpleJITLong(cp.getWideValueAt(cpindex)));
	    break;
	case CONSTANT_Double:
	    frame_.pushWide
		(new SimpleJITDouble
		 (Double.longBitsToDouble(cp.getWideValueAt(cpindex))));
	    break;
	case CONSTANT_String:
	    frame_.push(new SimpleJITReference(stringBP));
	    break;
	case CONSTANT_Binder:
	case CONSTANT_Reference:
	case CONSTANT_SharedState:
	    frame_.push(new SimpleJITReference());
	    break;
	default:
	    throw new Error("Unexpected CP tag: " + cp.getTagAt(cpindex));
	}
    }

    public void visit(Instruction.LocalWrite i) {
	int local_index = i.getLocalVariableOffset(buf);
	if (LOG_ENABLED) log(pc_ + " : LocalWrite " + i + " " + local_index);
        AbstractValue v = 
            (i.stackIns.length > 1 ? frame_.popWide() : frame_.pop());
        frame_.store(local_index, v);
    }
    public void visit(Instruction.LocalRead i) {
	int local_index = i.getLocalVariableOffset(buf);
	if (LOG_ENABLED) log(pc_ + " : LocalRead " + i + " " + local_index);
        AbstractValue v = frame_.load(local_index);
        if (i.stackOuts.length > 1)
	    frame_.pushWide(v);
        else
            frame_.push(v);
    }

    public void visit(Instruction.Switch i) {
	if (LOG_ENABLED) log(pc_ + " : Switch " + i);
	typeCheckInt(frame_.pop());
    }

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

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

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

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

    public void visit(Instruction.JSR i) {
	if (LOG_ENABLED) log(pc_ + " : JSR");
	frame_.push(new SimpleJITJumpTarget(pc_ + i.size(buf)));
    }

    public void visit(Instruction.JSR_W i) {
	if (LOG_ENABLED) log(pc_ + " : JSR_W");
	frame_.push(new SimpleJITJumpTarget(pc_ + i.size(buf)));
    }

    public void visit(Instruction.IfCmp i) {
	if (LOG_ENABLED) log(pc_ + " : IfCmp " + i);
	char tag = opcode2TypeCode(i.getOpcode());
	switch(tag) {
	case TypeCodes.INT:
	    typeCheckInt(frame_.pop());
	    typeCheckInt(frame_.pop());
	    break;
	case TypeCodes.REFERENCE:
	    typeCheckReference(frame_.pop());
	    typeCheckReference(frame_.pop());
	    break;
	default:
	    throw new OVMError("Unexpected type tag : " + tag);
	}
    }

    public void visit(Instruction.If i) {
	if (LOG_ENABLED) log(pc_ + " : If " + i);
	char tag = opcode2TypeCode(i.getOpcode());
	switch(tag) {
	case TypeCodes.INT:
	    typeCheckInt(frame_.pop());
	    break;
	case TypeCodes.REFERENCE:
	    /* Commented out the type check to accept the case where
	     * VM_Word/VM_Address's come into ifnull/ifnonnull */
    	    /*typeCheckReference(*/frame_.pop()/*)*/;
	    break;
	default:
	    throw new OVMError("Unexpected type tag : " + tag);
	}
    }

    public void visit(Instruction.LCMP i) {
	if (LOG_ENABLED) log(pc_ + " : LCMP");
	SimpleJITLong l2 = typeCheckLong(frame_.popWide());
	SimpleJITLong l1 = typeCheckLong(frame_.popWide());
	if (l1.isConstant() && l2.isConstant()) {
	    long l1v = l1.getConstantLong();
	    long l2v = l2.getConstantLong();
	    if (l1v > l2v)
		frame_.push(new SimpleJITInt(1));
	    else if (l1v < l2v)
		frame_.push(new SimpleJITInt(-1));
	    else
		frame_.push(new SimpleJITInt(0));
	} else
	    frame_.push(new SimpleJITInt());
    }

    private void visitFCMP(boolean G) {
	SimpleJITFloat d2 = typeCheckFloat(frame_.pop());
	SimpleJITFloat d1 = typeCheckFloat(frame_.pop());
	if (d1.isConstant() && d2.isConstant()) {
	    float d1v = d1.getConstantFloat();
	    float d2v = d2.getConstantFloat();
	    if (Float.isNaN(d1v) || Float.isNaN(d2v))
		if (G)
		    frame_.push(new SimpleJITInt(1));
		else
		    frame_.push(new SimpleJITInt(-1));
	    else if (d1v > d2v)
		frame_.push(new SimpleJITInt(1));
	    else if (d1v < d2v)
		frame_.push(new SimpleJITInt(-1));
	    else
		frame_.push(new SimpleJITInt(0));
	} else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.FCMPG i) {
	if (LOG_ENABLED) log(pc_ + " : FCMPG");
	visitFCMP(true);
    }

    public void visit(Instruction.FCMPL i) {
	if (LOG_ENABLED) log(pc_ + " : FCMPL");
	visitFCMP(false);
    }

    private void visitDCMP(boolean G) {
	SimpleJITDouble d2 = typeCheckDouble(frame_.popWide());
	SimpleJITDouble d1 = typeCheckDouble(frame_.popWide());
	if (d1.isConstant() && d2.isConstant()) {
	    double d1v = d1.getConstantDouble();
	    double d2v = d2.getConstantDouble();
	    if (Double.isNaN(d1v) || Double.isNaN(d2v))
		if (G)
		    frame_.push(new SimpleJITInt(1));
		else
		    frame_.push(new SimpleJITInt(-1));
	    else if (d1v > d2v)
		frame_.push(new SimpleJITInt(1));
	    else if (d1v < d2v)
		frame_.push(new SimpleJITInt(-1));
	    else
		frame_.push(new SimpleJITInt(0));
	} else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.DCMPG i) {
	if (LOG_ENABLED) log(pc_ + " : DCMPG");
	visitDCMP(true);
    }

    public void visit(Instruction.DCMPL i) {
	if (LOG_ENABLED) log(pc_ + " : DCMPL");
	visitDCMP(false);
    }

    private void visitArrayLoadPush(Frame frame,
				    char tag, boolean resolved, 
				    Blueprint bp, TypeName typeName) {
	switch(tag) {
	case TypeCodes.BYTE:
	case TypeCodes.BOOLEAN:
	case TypeCodes.CHAR:
	case TypeCodes.SHORT:
	case TypeCodes.INT:
	    frame.push(new SimpleJITInt());
	    break;
	case TypeCodes.LONG:
	    frame.pushWide(new SimpleJITLong());
	    break;
	case TypeCodes.FLOAT:
	    frame.push(new SimpleJITFloat());
	    break;
	case TypeCodes.DOUBLE:
	    frame.pushWide(new SimpleJITDouble());
	    break;
	case TypeCodes.OBJECT:
	case TypeCodes.REFERENCE:
	    if (resolved)
		frame.push(new SimpleJITReference(bp));
	    else if (typeName != null)
		frame.push(new SimpleJITReference(typeName.asCompound()));
	    else
		frame.push(new SimpleJITReference());
	    break;
	case TypeCodes.ARRAY:
	    if (resolved)
		frame.push(new SimpleJITArray(bp.asArray()));
	    else if (typeName != null)
		frame.push(new SimpleJITArray(typeName.asArray()));
	    else
		frame.push(new SimpleJITArray());
	    break;
	case TypeCodes.VOID:
	    break;
	default:
	    throw new OVMError("Unexpected type tag : " + tag);
	}
	
    }

    public void visit(Instruction.ArrayLoad i) {
	if (LOG_ENABLED) log(pc_ + " : ArrayLoad " + i);
	SimpleJITInt index = typeCheckInt(frame_.pop());
	SimpleJITReference ref = typeCheckReference(frame_.pop());

	if (ref.isArray()) {
	    SimpleJITArray raref = (SimpleJITArray)ref;
	    if (index.isConstant() && 
		raref.isLengthConstant() &&
		(index.getConstantInt() >= raref.getConstantLength()
		 || index.getConstantInt() < 0))
		if (LOG_ENABLED) log("Found an array out of bound case");
	} 
	if (ref.isNull())
	    if (LOG_ENABLED) log("Found a null pointer exception case");

	if (ref.typeUnknown()) {
	    char tag = opcode2TypeCode(i.getOpcode());
	    visitArrayLoadPush(frame_, tag, false, null, null);
	} else if (ref.resolved()) {
	    SimpleJITArray rec = 
		(SimpleJITArray)ref;
	    Blueprint.Array bpa = rec.getBlueprint().asArray();
	    Blueprint componentBP = bpa.getComponentBlueprint();
	    Type componentType = componentBP.getType();
	    char componentTag = componentType.getUnrefinedName().getTypeTag();
	    visitArrayLoadPush(frame_, componentTag, true, componentBP, null);
	} else {
	    SimpleJITArray rec =
		(SimpleJITArray)ref;
	    TypeName.Array arrayTypeName = rec.getTypeName().asArray();
	    TypeName componentTypeName = arrayTypeName.getComponentTypeName();
	    char componentTag = componentTypeName.getTypeTag();
	    visitArrayLoadPush(frame_, componentTag, false, null, componentTypeName);
	}
    }

    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.ArrayAccess i) {
	if (LOG_ENABLED) log(pc_ + " : ArrayAccess");
	throw new OVMError.UnsupportedOperation();
    }	

    public void visit(Instruction.ArrayStore i) {
	if (LOG_ENABLED) log(pc_ + " : ArrayStore " + i);
	int opcode = i.getOpcode();
	switch(opcode) {
	case Opcodes.IASTORE:
	case Opcodes.BASTORE:
	case Opcodes.CASTORE:
	case Opcodes.SASTORE:
	    typeCheckInt(frame_.pop());
	    break;
	case Opcodes.LASTORE:
	    typeCheckLong(frame_.popWide());
	    break;
	case Opcodes.FASTORE:
	    typeCheckFloat(frame_.pop());
	    break;
	case Opcodes.DASTORE:
	    typeCheckDouble(frame_.popWide());
	    break;
	case Opcodes.AASTORE:
	case Opcodes.UNCHECKED_AASTORE:
	case Opcodes.AASTORE_WITH_BARRIER:
	    typeCheckReference(frame_.pop());
	    break;
	default:
	    throw new OVMError("Unexpected array store opcode : " + opcode);
	}
	    
	SimpleJITInt index = typeCheckInt(frame_.pop());
	SimpleJITReference ref = typeCheckReference(frame_.pop());
	if (ref.isArray()) {
	    SimpleJITArray raref = (SimpleJITArray)ref;
	    if (index.isConstant() && 
		raref.isLengthConstant() &&
		(index.getConstantInt() >= raref.getConstantLength()
		 || index.getConstantInt() < 0))
		if (LOG_ENABLED) log("Found an array out of bound case");
	} 
	if (ref.isNull())
	    if (LOG_ENABLED) log("Found a null pointer exception case");
    }

    public void visit(Instruction.IADD i) {
	if (LOG_ENABLED) log(pc_ + " : IADD");
	SimpleJITInt i1 = typeCheckInt(frame_.pop());
	SimpleJITInt i2 = typeCheckInt(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    frame_.push(new SimpleJITInt(i1.getConstantInt() 
					 + i2.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.ISUB i) {
	if (LOG_ENABLED) log(pc_ + " : ISUB");
	SimpleJITInt i1 = typeCheckInt(frame_.pop());
	SimpleJITInt i2 = typeCheckInt(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    frame_.push(new SimpleJITInt(i2.getConstantInt() 
					- i1.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.IMUL i) {
	if (LOG_ENABLED) log(pc_ + " : IMUL");
	SimpleJITInt i1 = typeCheckInt(frame_.pop());
	SimpleJITInt i2 = typeCheckInt(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    frame_.push(new SimpleJITInt(i1.getConstantInt() 
					* i2.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.IDIV i) {
	if (LOG_ENABLED) log(pc_ + " : IDIV");
	SimpleJITInt i1 = typeCheckInt(frame_.pop());
	SimpleJITInt i2 = typeCheckInt(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    if (i1.getConstantInt() == 0) {
		if (LOG_ENABLED) log("Division-by-zero found");
		frame_.push(new SimpleJITInt());
	    } else 		
		frame_.push(new SimpleJITInt(i2.getConstantInt() 
					    / i1.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.IREM i) {
	if (LOG_ENABLED) log(pc_ + " : IREM");
	SimpleJITInt i1 = typeCheckInt(frame_.pop());
	SimpleJITInt i2 = typeCheckInt(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    if (i1.getConstantInt() == 0) {
		if (LOG_ENABLED) log("Division-by-zero found");
		frame_.push(new SimpleJITInt());
	    } else 		
		frame_.push(new SimpleJITInt(i2.getConstantInt() 
					    % i1.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.ISHL i) {
	if (LOG_ENABLED) log(pc_ + " : ISHL");
	SimpleJITInt width = typeCheckInt(frame_.pop());
	SimpleJITInt value = typeCheckInt(frame_.pop());
	if (width.isConstant() && value.isConstant())
	    frame_.push(new SimpleJITInt(value.getConstantInt() << 
					width.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.ISHR i) {
	if (LOG_ENABLED) log(pc_ + " : ISHR");
	SimpleJITInt width = typeCheckInt(frame_.pop());
	SimpleJITInt value = typeCheckInt(frame_.pop());
	if (width.isConstant() && value.isConstant())
	    frame_.push(new SimpleJITInt(value.getConstantInt() >> 
					width.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.IUSHR i) {
	if (LOG_ENABLED) log(pc_ + " : IUSHR");
	SimpleJITInt width = typeCheckInt(frame_.pop());
	SimpleJITInt value = typeCheckInt(frame_.pop());
	if (width.isConstant() && value.isConstant())
	    frame_.push(new SimpleJITInt(value.getConstantInt() >>> 
					width.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.LSHL i) {
	if (LOG_ENABLED) log(pc_ + " : LSHL");
	SimpleJITInt width = typeCheckInt(frame_.pop());
	SimpleJITLong value = typeCheckLong(frame_.popWide());
	if (width.isConstant() && value.isConstant())
	    frame_.pushWide(new SimpleJITLong(value.getConstantLong() << 
					     width.getConstantInt()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.LSHR i) {
	if (LOG_ENABLED) log(pc_ + " : LSHR");
	SimpleJITInt width = typeCheckInt(frame_.pop());
	SimpleJITLong value = typeCheckLong(frame_.popWide());
	if (width.isConstant() && value.isConstant())
	    frame_.pushWide(new SimpleJITLong(value.getConstantLong() >> 
					     width.getConstantInt()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.LUSHR i) {
	if (LOG_ENABLED) log(pc_ + " : LUSHR");
	SimpleJITInt width = typeCheckInt(frame_.pop());
	SimpleJITLong value = typeCheckLong(frame_.popWide());
	if (width.isConstant() && value.isConstant())
	    frame_.pushWide(new SimpleJITLong(value.getConstantLong() >>> 
					     width.getConstantInt()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }


    public void visit(Instruction.INEG i) {
	if (LOG_ENABLED) log(pc_ + " : INEG");
	SimpleJITInt i1 = typeCheckInt(frame_.pop());
	if (i1.isConstant())
	    frame_.push(new SimpleJITInt(- i1.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.IAND i) {
	if (LOG_ENABLED) log(pc_ + " : IAND");
	SimpleJITInt i1 = typeCheckInt(frame_.pop());
	SimpleJITInt i2 = typeCheckInt(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    frame_.push(new SimpleJITInt(i1.getConstantInt() 
					& i2.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.IOR i) {
	if (LOG_ENABLED) log(pc_ + " : IOR");
	SimpleJITInt i1 = typeCheckInt(frame_.pop());
	SimpleJITInt i2 = typeCheckInt(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    frame_.push(new SimpleJITInt(i1.getConstantInt() 
					 | i2.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.IXOR i) {
	if (LOG_ENABLED) log(pc_ + " : IXOR");
	SimpleJITInt i1 = typeCheckInt(frame_.pop());
	SimpleJITInt i2 = typeCheckInt(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    frame_.push(new SimpleJITInt(i1.getConstantInt() 
					^ i2.getConstantInt()));
	else
	    frame_.push(new SimpleJITInt());
    }



    public void visit(Instruction.LADD i) {
	if (LOG_ENABLED) log(pc_ + " : LADD");
	SimpleJITLong i1 = typeCheckLong(frame_.popWide());
	SimpleJITLong i2 = typeCheckLong(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    frame_.pushWide(new SimpleJITLong(i1.getConstantLong() 
					     + i2.getConstantLong()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.LSUB i) {
	if (LOG_ENABLED) log(pc_ + " : LSUB");
	SimpleJITLong i1 = typeCheckLong(frame_.popWide());
	SimpleJITLong i2 = typeCheckLong(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    frame_.pushWide(new SimpleJITLong(i2.getConstantLong() 
					     - i1.getConstantLong()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.LMUL i) {
	if (LOG_ENABLED) log(pc_ + " : LMUL");
	SimpleJITLong i1 = typeCheckLong(frame_.popWide());
	SimpleJITLong i2 = typeCheckLong(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    frame_.pushWide(new SimpleJITLong(i1.getConstantLong() 
					     * i2.getConstantLong()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.LDIV i) {
	if (LOG_ENABLED) log(pc_ + " : LDIV");
	SimpleJITLong i1 = typeCheckLong(frame_.popWide());
	SimpleJITLong i2 = typeCheckLong(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    if (i1.getConstantLong() == 0L) {
		if (LOG_ENABLED) log("Division-by-zero found");
		frame_.pushWide(new SimpleJITLong());
	    } else 		
		frame_.pushWide(new SimpleJITLong(i2.getConstantLong() 
						 / i1.getConstantLong()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.LREM i) {
	if (LOG_ENABLED) log(pc_ + " : LREM");
	SimpleJITLong i1 = typeCheckLong(frame_.popWide());
	SimpleJITLong i2 = typeCheckLong(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    if (i1.getConstantLong() == 0L) {
		if (LOG_ENABLED) log("Division-by-zero found");
		frame_.pushWide(new SimpleJITLong());
	    } else 		
		frame_.pushWide(new SimpleJITLong(i2.getConstantLong() 
						 % i1.getConstantLong()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.LNEG i) {
	if (LOG_ENABLED) log(pc_ + " : LNEG");
	SimpleJITLong i1 = typeCheckLong(frame_.popWide());
	if (i1.isConstant())
	    frame_.pushWide(new SimpleJITLong(- i1.getConstantLong()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.LAND i) {
	if (LOG_ENABLED) log(pc_ + " : LAND");
	SimpleJITLong i1 = typeCheckLong(frame_.popWide());
	SimpleJITLong i2 = typeCheckLong(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    frame_.pushWide(new SimpleJITLong(i1.getConstantLong() 
					     & i2.getConstantLong()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.LOR i) {
	if (LOG_ENABLED) log(pc_ + " : LOR");
	SimpleJITLong i1 = typeCheckLong(frame_.popWide());
	SimpleJITLong i2 = typeCheckLong(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    frame_.pushWide(new SimpleJITLong(i1.getConstantLong() 
					     | i2.getConstantLong()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.LXOR i) {
	if (LOG_ENABLED) log(pc_ + " : LXOR");
	SimpleJITLong i1 = typeCheckLong(frame_.popWide());
	SimpleJITLong i2 = typeCheckLong(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    frame_.pushWide(new SimpleJITLong(i1.getConstantLong() 
					     ^ i2.getConstantLong()));
	else
	    frame_.pushWide(new SimpleJITLong());
    }


    public void visit(Instruction.FNEG i) {
	if (LOG_ENABLED) log(pc_ + " : FNEG");
	SimpleJITFloat i1 = typeCheckFloat(frame_.pop());
	if (i1.isConstant())
	    frame_.push(new SimpleJITFloat(- i1.getConstantFloat()));
	else
	    frame_.push(new SimpleJITFloat());
    }

    public void visit(Instruction.FADD i) {
	if (LOG_ENABLED) log(pc_ + " : FADD");
	SimpleJITFloat i1 = typeCheckFloat(frame_.pop());
	SimpleJITFloat i2 = typeCheckFloat(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    frame_.push(new SimpleJITFloat(i1.getConstantFloat() 
					  + i2.getConstantFloat()));
	else
	    frame_.push(new SimpleJITFloat());
    }

    public void visit(Instruction.FSUB i) {
	if (LOG_ENABLED) log(pc_ + " : FSUB");
	SimpleJITFloat i1 = typeCheckFloat(frame_.pop());
	SimpleJITFloat i2 = typeCheckFloat(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    frame_.push(new SimpleJITFloat(i2.getConstantFloat() 
					  - i1.getConstantFloat()));
	else
	    frame_.push(new SimpleJITFloat());
    }

    public void visit(Instruction.FMUL i) {
	if (LOG_ENABLED) log(pc_ + " : FMUL");
	SimpleJITFloat i1 = typeCheckFloat(frame_.pop());
	SimpleJITFloat i2 = typeCheckFloat(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    frame_.push(new SimpleJITFloat(i1.getConstantFloat() 
					  * i2.getConstantFloat()));
	else
	    frame_.push(new SimpleJITFloat());
    }

    public void visit(Instruction.FDIV i) {
	if (LOG_ENABLED) log(pc_ + " : FDIV");
	SimpleJITFloat i1 = typeCheckFloat(frame_.pop());
	SimpleJITFloat i2 = typeCheckFloat(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    if (i1.getConstantFloat() == 0.0F) {
		if (LOG_ENABLED) log("Division-by-zero found");
		frame_.push(new SimpleJITFloat());
	    } else 		
		frame_.push(new SimpleJITFloat(i2.getConstantFloat() 
					      / i1.getConstantFloat()));
	else
	    frame_.push(new SimpleJITFloat());
    }

    public void visit(Instruction.FREM i) {
	if (LOG_ENABLED) log(pc_ + " : FREM");
	SimpleJITFloat i1 = typeCheckFloat(frame_.pop());
	SimpleJITFloat i2 = typeCheckFloat(frame_.pop());
	if (i1.isConstant() && i2.isConstant())
	    if (i1.getConstantFloat() == 0.0F) {
		if (LOG_ENABLED) log("Division-by-zero found");
		frame_.push(new SimpleJITFloat());
	    } else 		
		frame_.push(new SimpleJITFloat(i2.getConstantFloat() 
					      % i1.getConstantFloat()));
	else
	    frame_.push(new SimpleJITFloat());
    }


    public void visit(Instruction.DNEG i) {
	if (LOG_ENABLED) log(pc_ + " : DNEG");
	SimpleJITDouble i1 = typeCheckDouble(frame_.popWide());
	if (i1.isConstant())
	    frame_.pushWide(new SimpleJITDouble(- i1.getConstantDouble()));
	else
	    frame_.pushWide(new SimpleJITDouble());
    }

    public void visit(Instruction.DADD i) {
	if (LOG_ENABLED) log(pc_ + " : DADD");
	SimpleJITDouble i1 = typeCheckDouble(frame_.popWide());
	SimpleJITDouble i2 = typeCheckDouble(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    frame_.pushWide(new SimpleJITDouble(i1.getConstantDouble() 
					       + i2.getConstantDouble()));
	else
	    frame_.pushWide(new SimpleJITDouble());
    }

    public void visit(Instruction.DSUB i) {
	if (LOG_ENABLED) log(pc_ + " : DSUB");
	SimpleJITDouble i1 = typeCheckDouble(frame_.popWide());
	SimpleJITDouble i2 = typeCheckDouble(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    frame_.pushWide(new SimpleJITDouble(i2.getConstantDouble() 
					       - i1.getConstantDouble()));
	else
	    frame_.pushWide(new SimpleJITDouble());
    }

    public void visit(Instruction.DMUL i) {
	if (LOG_ENABLED) log(pc_ + " : DMUL");
	SimpleJITDouble i1 = typeCheckDouble(frame_.popWide());
	SimpleJITDouble i2 = typeCheckDouble(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    frame_.pushWide(new SimpleJITDouble(i1.getConstantDouble() 
					       * i2.getConstantDouble()));
	else
	    frame_.pushWide(new SimpleJITDouble());
    }

    public void visit(Instruction.DDIV i) {
	if (LOG_ENABLED) log(pc_ + " : DDIV");
	SimpleJITDouble i1 = typeCheckDouble(frame_.popWide());
	SimpleJITDouble i2 = typeCheckDouble(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    if (i1.getConstantDouble() == 0.0) {
		if (LOG_ENABLED) log("Division-by-zero found");
		frame_.pushWide(new SimpleJITDouble());
	    } else 		
		frame_.pushWide(new SimpleJITDouble(i2.getConstantDouble() 
						   / i1.getConstantDouble()));
	else
	    frame_.pushWide(new SimpleJITDouble());
    }

    public void visit(Instruction.DREM i) {
	if (LOG_ENABLED) log(pc_ + " : DREM");
	SimpleJITDouble i1 = typeCheckDouble(frame_.popWide());
	SimpleJITDouble i2 = typeCheckDouble(frame_.popWide());
	if (i1.isConstant() && i2.isConstant())
	    if (i1.getConstantDouble() == 0.0) {
		if (LOG_ENABLED) log("Division-by-zero found");
		frame_.pushWide(new SimpleJITDouble());
	    } else 		
		frame_.pushWide(new SimpleJITDouble(i2.getConstantDouble() 
						   % i1.getConstantDouble()));
	else
	    frame_.pushWide(new SimpleJITDouble());
    }

    public void visit(Instruction.I2L i) {
	if (LOG_ENABLED) log(pc_ + " : I2L");
	SimpleJITInt in = typeCheckInt(frame_.pop());
	if (in.isConstant()) {
	    int c = in.getConstantInt();
	    frame_.pushWide(new SimpleJITLong(c));
	} else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.I2F i) {
	if (LOG_ENABLED) log(pc_ + " : I2F");
	SimpleJITInt in = typeCheckInt(frame_.pop());
	if (in.isConstant()) {
	    int c = in.getConstantInt();
	    frame_.push(new SimpleJITFloat(c));
	} else
	    frame_.push(new SimpleJITFloat());
    }

    public void visit(Instruction.I2D i) {
	if (LOG_ENABLED) log(pc_ + " : I2D");
	SimpleJITInt in = typeCheckInt(frame_.pop());
	if (in.isConstant()) {
	    int c = in.getConstantInt();
	    frame_.pushWide(new SimpleJITDouble(c));
	} else
	    frame_.pushWide(new SimpleJITDouble());
    }

    public void visit(Instruction.I2C i) {
	if (LOG_ENABLED) log(pc_ + " : I2C");
	SimpleJITInt in = typeCheckInt(frame_.pop());
	if (in.isConstant()) {
	    int c = in.getConstantInt();
	    frame_.push(new SimpleJITInt((char)c));
	} else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.I2S i) {
	if (LOG_ENABLED) log(pc_ + " : I2S");
	SimpleJITInt in = typeCheckInt(frame_.pop());
	if (in.isConstant()) {
	    int c = in.getConstantInt();
	    frame_.push(new SimpleJITInt((short)c));
	} else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.I2B i) {
	if (LOG_ENABLED) log(pc_ + " : I2B");
	SimpleJITInt in = typeCheckInt(frame_.pop());
	if (in.isConstant()) {
	    int c = in.getConstantInt();
	    frame_.push(new SimpleJITInt((byte)c));
	} else
	    frame_.push(new SimpleJITInt());
    }


    public void visit(Instruction.L2I i) {
	if (LOG_ENABLED) log(pc_ + " : L2I");
	SimpleJITLong in = typeCheckLong(frame_.popWide());
	if (in.isConstant()) {
	    long c = in.getConstantLong();
	    frame_.push(new SimpleJITInt((int)c));
	} else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.L2F i) {
	if (LOG_ENABLED) log(pc_ + " : I2F");
	SimpleJITLong in = typeCheckLong(frame_.popWide());
	if (in.isConstant()) {
	    long c = in.getConstantLong();
	    frame_.push(new SimpleJITFloat(c));
	} else
	    frame_.push(new SimpleJITFloat());
    }

    public void visit(Instruction.L2D i) {
	if (LOG_ENABLED) log(pc_ + " : I2D");
	SimpleJITLong in = typeCheckLong(frame_.popWide());
	if (in.isConstant()) {
	    long c = in.getConstantLong();
	    frame_.pushWide(new SimpleJITDouble(c));
	} else
	    frame_.pushWide(new SimpleJITDouble());
    }


    public void visit(Instruction.F2I i) {
	if (LOG_ENABLED) log(pc_ + " : F2I");
	SimpleJITFloat in = typeCheckFloat(frame_.pop());
	if (in.isConstant()) {
	    float c = in.getConstantFloat();
	    frame_.push(new SimpleJITInt((int)c));
	} else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.F2L i) {
	if (LOG_ENABLED) log(pc_ + " : F2L");
	SimpleJITFloat in = typeCheckFloat(frame_.pop());
	if (in.isConstant()) {
	    float c = in.getConstantFloat();
	    frame_.pushWide(new SimpleJITLong((long)c));
	} else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.F2D i) {
	if (LOG_ENABLED) log(pc_ + " : F2D");
	SimpleJITFloat in = typeCheckFloat(frame_.pop());
	if (in.isConstant()) {
	    float c = in.getConstantFloat();
	    frame_.pushWide(new SimpleJITDouble(c));
	} else
	    frame_.pushWide(new SimpleJITDouble());
    }

    public void visit(Instruction.D2I i) {
	if (LOG_ENABLED) log(pc_ + " : D2I");
	SimpleJITDouble in = typeCheckDouble(frame_.popWide());
	if (in.isConstant()) {
	    double c = in.getConstantDouble();
	    frame_.push(new SimpleJITInt((int)c));
	} else
	    frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.D2L i) {
	if (LOG_ENABLED) log(pc_ + " : D2L");
	SimpleJITDouble in = typeCheckDouble(frame_.popWide());
	if (in.isConstant()) {
	    double c = in.getConstantDouble();
	    frame_.pushWide(new SimpleJITLong((long)c));
	} else
	    frame_.pushWide(new SimpleJITLong());
    }

    public void visit(Instruction.D2F i) {
	if (LOG_ENABLED) log(pc_ + " : D2F");
	SimpleJITDouble in = typeCheckDouble(frame_.popWide());
	if (in.isConstant()) {
	    double c = in.getConstantDouble();
	    frame_.push(new SimpleJITFloat((float)c));
	} else
	    frame_.push(new SimpleJITFloat());
    }

    public void visit(Instruction.ARRAYLENGTH i) {
	if (LOG_ENABLED) log(pc_ + " : ARRAYLENGTH");
	SimpleJITValue v = (SimpleJITValue)frame_.pop();

	if (v instanceof SimpleJITArray) {
	    SimpleJITArray arr = 
		(SimpleJITArray)v;
	    if (arr.isLengthConstant()) {
		int len = arr.getConstantLength();
		frame_.push(new SimpleJITInt(len));
	    } else
		frame_.push(new SimpleJITInt());
	    if (arr.isNull())
		if (LOG_ENABLED) log("Found a null pointer exception case");
	} else if (v instanceof SimpleJITReference
		   && ((SimpleJITReference)v).isNullOrUnknownType()) {
	    frame_.push(new SimpleJITInt());
	    if (((SimpleJITReference)v).isNull)
		if (LOG_ENABLED) log("Found a null pointer exception case");
	} else
	    throw new OVMError("Unexpected type");
    }

    // Used in reflection
    public void visit(Instruction.GETFIELD_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : GETFIELD_QUICK");

// 	if (true)
// 	    throw new NeedsFiatError("GETFIELD_QUICK");

	int offset = i.getOffset(buf);
	SimpleJITReference receiver = 
	    typeCheckReference(frame_.pop());
	if (receiver.resolved()) {
	    try {
		S3Blueprint bp = (S3Blueprint)targetDomain
		    .blueprintFor(receiver.getType());
		Field f = bp.fieldAt(offset);
		if (f == null) {
		    frame_.push(new SimpleJITInt());
		} else {
		    Type ft = f.getType();
		    OVMBase.OVMassert(ft.isPrimitive());
		    Type.Primitive ftp = (Type.Primitive)ft;
		    char tag = ftp.getName().getTypeTag();
		    switch(tag) {
		    case TypeCodes.INT:
		    case TypeCodes.CHAR:
		    case TypeCodes.SHORT:
		    case TypeCodes.BYTE:
		    case TypeCodes.BOOLEAN:
			frame_.push(new SimpleJITInt());
			break;
		    case TypeCodes.FLOAT:
			frame_.push(new SimpleJITFloat());
			break;
		    default:
			throw new OVMError
			    ("Unexpected type tag : " + tag);
		    }
		}
	    } catch (LinkageException e) {
		throw e.unchecked();
	    }
	} else { // FIXME : not enough type information
	    frame_.push(new SimpleJITInt());
	}
    }

    // Used in VM_Address operations
    public void visit(Instruction.REF_GETFIELD_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : REF_GETFIELD_QUICK");

// 	if (true)
// 	    throw new NeedsFiatError("REF_GETFIELD_QUICK");

	int offset = i.getOffset(buf);
	SimpleJITReference receiver = 
	    typeCheckReference(frame_.pop());
	if (receiver.resolved()) {
	    try {
		S3Blueprint bp = 
		    (S3Blueprint)receiver.getBlueprint();
		Field f = bp.fieldAt(offset);
		if (f == null)
		    frame_.push(new SimpleJITReference());
		else {
		    Type ftype = f.getType();
		    Blueprint fbp = 
			blueprintFor(targetDomain, 
				     ftype.asCompound());
		    OVMBase.OVMassert(ftype.isCompound());
		    if (ftype.isArray())
			frame_.push
			    (new SimpleJITArray(fbp.asArray()));
		    else
			frame_.push
			    (new SimpleJITReference(fbp));
		}
	    } catch (LinkageException e) {
		throw e.unchecked();
	    }
	} else { // FIXME : not enough type information
	    frame_.push(new SimpleJITReference());
	}
    }

    // Used in reflection
    public void visit(Instruction.GETFIELD2_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : GETFIELD2_QUICK");

// 	if (true)
// 	    throw new NeedsFiatError("GETFIELD2_QUICK");

	int offset = i.getOffset(buf);
	SimpleJITReference receiver = 
	    typeCheckReference(frame_.pop());
 	if (receiver.resolved()) {
	    try {
		S3Blueprint bp = (S3Blueprint)targetDomain
		    .blueprintFor(receiver.getType());
		Field f = bp.fieldAt(offset);
		if (f == null) {
		    frame_.pushWide(new SimpleJITLong());
		} else {
		    Type ft = f.getType();
		    OVMBase.OVMassert(ft.isWidePrimitive());
		    Type.Primitive ftp = (Type.Primitive)ft;
		    char tag = ftp.getName().getTypeTag();
		    switch(tag) {
		    case TypeCodes.LONG:
			frame_.pushWide(new SimpleJITLong());
			break;
		    case TypeCodes.DOUBLE:
			frame_.pushWide(new SimpleJITDouble());
			break;
		    default:
			throw new OVMError("Unexpected type tag : " + tag);
		    }
		}
	    } catch (LinkageException e) {
		throw e.unchecked();
	    }
	} else { // FIXME : not enough type information
	    frame_.pushWide(new SimpleJITLong());
	}
    }

    // Used in reflection
    public void visit(Instruction.PUTFIELD_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : PUTFIELD_QUICK");

// 	if (true)
// 	    throw new NeedsFiatError("PUTFIELD_QUICK");

	int offset = i.getOffset(buf);
	SimpleJITValue obj = 
	    (SimpleJITValue)frame_.pop();
	SimpleJITReference receiver = 
	    typeCheckReference(frame_.pop());
    }

    // Used in reflection
    public void visit(Instruction.PUTFIELD2_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : PUTFIELD2_QUICK");

// 	if (true)
// 	    throw new NeedsFiatError("PUTFIELD2_QUICK");

	int offset = i.getOffset(buf);
	SimpleJITValue obj = 
	    (SimpleJITValue)frame_.popWide();
	SimpleJITReference receiver = 
	    typeCheckReference(frame_.pop());
    }

    private void visitInvocationReturnValue(Descriptor.Method desc) {
	TypeName returnTypeName = desc.getType();
	char returnTypeTag = returnTypeName.getTypeTag();
	Blueprint returnBP = returnTypeName.isCompound()
	    ? blueprintFor(targetDomain, 
			   returnTypeName.asCompound())
	    : null;
	switch(returnTypeTag) {
	case TypeCodes.BYTE:
	case TypeCodes.BOOLEAN:
	case TypeCodes.CHAR:
	case TypeCodes.SHORT:
	case TypeCodes.INT:
	    frame_.push(new SimpleJITInt());
	    break;
	case TypeCodes.LONG:
	    frame_.pushWide(new SimpleJITLong());
	    break;
	case TypeCodes.FLOAT:
	    frame_.push(new SimpleJITFloat());
	    break;
	case TypeCodes.DOUBLE:
	    frame_.pushWide(new SimpleJITDouble());
	    break;
	case TypeCodes.OBJECT:
	    if (returnBP != null)
		frame_.push(new SimpleJITReference(returnBP));
	    else
		frame_.push(new SimpleJITReference
			   (returnTypeName.asCompound()));
	    break;
	case TypeCodes.ARRAY:
	    if (returnBP != null)
		frame_.push(new SimpleJITArray(returnBP.asArray()));
	    else
		frame_.push(new SimpleJITArray(returnTypeName.asArray()));
	    break;
	case TypeCodes.VOID:
	    break;
	default:
	    throw new OVMError("Unexpected type tag : " + returnTypeTag);
	}
    }

    public void visit(Instruction.INVOKESUPER_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : INVOKESUPER_QUICK");

	if (true)
	    throw new NeedsFiatError("INVOKESUPER_QUICK");

	int method_index = i.getMethodTableIndex(buf);
	int arg_length = i.getArgumentLengthInWords(buf);
	SimpleJITValue[] arguments = new SimpleJITValue[arg_length + 1];
	for(int j = arg_length; j >= 0; j--)
	    arguments[j] = (SimpleJITValue)frame_.pop();
      	SimpleJITReference receiver = (SimpleJITReference)arguments[0];
	OVMBase.OVMassert(receiver.resolved());
	S3Blueprint bp = (S3Blueprint)receiver.getBlueprint();
	S3ByteCode[] vTable = (S3ByteCode[])bp.getVTable();
	S3ByteCode bc = vTable[method_index];
	Descriptor.Method desc = bc.getSelector().getDescriptor();
	visitInvocationReturnValue(desc);
    }

    // Used in boot calls
    public void visit(Instruction.INVOKENONVIRTUAL_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : INVOKENONVIRTUAL_QUICK");

// 	if (true)
// 	    throw new NeedsFiatError("INVOKENONVIRTUAL_QUICK");

	int method_index = i.getMethodTableIndex(buf);
	int arg_length = i.getArgumentLengthInWords(buf);
	Selector.Method sel = i.getSelector(buf, cp);
	SimpleJITValue[] arguments = new SimpleJITValue[arg_length + 1];
	for(int j = arg_length; j >= 0; j--)
	    arguments[j] = (SimpleJITValue)frame_.pop();
	if (arguments[0] instanceof SimpleJITReference) {
	    visitInvocationReturnValue(sel.getDescriptor());
// 	    SimpleJITReference receiver = 
// 		(SimpleJITReference)arguments[0];
// 	    S3Blueprint bp = (S3Blueprint)receiver.getBlueprint();
// 	    S3ByteCode[] nvTable = (S3ByteCode[])bp.getNVTable();
// 	    S3ByteCode bc = nvTable[method_index];
// 	    Descriptor.Method desc = bc.getSelector().getDescriptor();
// 	    visitInvocationReturnValue(desc);
	} else 
	    throw new OVMError("receiver is type " + arguments[0]);
    }

    public void visit(Instruction.INVOKENONVIRTUAL2_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : INVOKENONVIRTUAL2_QUICK");

	if (true)
	    throw new NeedsFiatError("INVOKENONVIRTUAL2_QUICK");

	/*
	int linkset_index = i.getLinkSetIndex(buf);
	int method_index = i.getMethodTableIndex(buf);
	int arg_length = i.getArgumentLengthInWords(buf);
	SimpleJITValue[] arguments = new SimpleJITValue[arg_length + 1];
	for(int j = arg_length; j >= 0; j--)
           arguments[j] = (SimpleJITValue)frame_.pop();
	*/
	/*
      	SimpleJITReference this_or_shst = 
	    (SimpleJITReference)frame_.load(0);
	OVMBase.OVMassert(this_or_shst.resolved());
	S3Blueprint this_or_shst_bp = (S3Blueprint)this_or_shst.getBlueprint();
	S3Blueprint bp = this_or_shst.getBlueprintForQuickIndex(linkset_index);
	*/
	/*
	S3Blueprint bp = declaringTypeBP.getBlueprintForQuickIndex(linkset_index);
	S3ByteCode[] nvTable = (S3ByteCode[])bp.getNVTable();
	S3ByteCode bc = nvTable[method_index];
	Descriptor.Method desc = bc.getSelector().getDescriptor();
	visitInvocationReturnValue(desc);
	*/
    }

    public void visit(Instruction.INVOKEINTERFACE_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : INVOKEINTERFACE_QUICK");

	if (true)
	    throw new NeedsFiatError("INVOKEINTERFACE_QUICK");

	int method_index = i.getMethodTableIndex(buf);
	int arg_length = i.getArgumentLengthInWords(buf);
	SimpleJITValue[] arguments = new SimpleJITValue[arg_length + 1];
	for(int j = arg_length; j >= 0; j--)
	    arguments[j] = (SimpleJITValue)frame_.pop();
      	SimpleJITReference receiver = (SimpleJITReference)arguments[0];
	S3Blueprint bp = (S3Blueprint)receiver.getBlueprint();
	OVMBase.OVMassert(receiver.resolved());
	S3Method m = bp.lookupMethod(method_index, "ifTable");
	S3ByteCode bc = (S3ByteCode)m.getCode(S3ByteCode.KIND);
	Descriptor.Method desc = bc.getSelector().getDescriptor();
	visitInvocationReturnValue(desc);
    }

    // Used in kernel calls
    public void visit(Instruction.INVOKEVIRTUAL_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : INVOKEVIRTUAL_QUICK");

// 	if (true)
// 	    throw new NeedsFiatError("INVOKEVIRTUAL_QUICK");

	int method_index = i.getMethodTableIndex(buf);
	int arg_length = i.getArgumentLengthInWords(buf);
	SimpleJITValue[] arguments = new SimpleJITValue[arg_length + 1];
	for(int j = arg_length; j >= 0; j--)
	    arguments[j] = (SimpleJITValue)frame_.pop();
      	//SimpleJITReference receiver = (SimpleJITReference)arguments[0];
	Selector.Method sel = i.getSelector(buf, cp);
	visitInvocationReturnValue(sel.getDescriptor());

// 	S3Blueprint bp = (S3Blueprint)receiver.getBlueprint();
// 	OVMBase.OVMassert(receiver.resolved());
// 	S3ByteCode[] vTable = (S3ByteCode[])bp.getVTable();
// 	S3ByteCode bc = vTable[method_index];
// 	Descriptor.Method desc = bc.getSelector().getDescriptor();
// 	visitInvocationReturnValue(desc);
    }

    public void visit(Instruction.Invocation i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf));
	Selector.Method sel = i.getSelector(buf, cp);
	Descriptor.Method desc = sel.getDescriptor();
	int formalArgumentCount = desc.getArgumentCount();
	for(int j = formalArgumentCount - 1; j >= 0; j--) {
	    TypeName tn = desc.getArgumentType(j);
	    popAndTypeCheck(tn, frame_);
	}
	SimpleJITReference receiver = typeCheckReference(frame_.pop());
	visitInvocationReturnValue(desc);
    }

    public void visit(Instruction.INVOKEVIRTUAL i) {
	visit((Instruction.Invocation)i);
    }

    public void visit(Instruction.INVOKESPECIAL i) {
	visit((Instruction.Invocation)i);
    }

    public void visit(Instruction.INVOKESTATIC i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf));
	Selector.Method sel = i.getSelector(buf, cp);
	Descriptor.Method desc = sel.getDescriptor();
	int formalArgumentCount = desc.getArgumentCount();
	for(int j = formalArgumentCount - 1; j >= 0; j--) {
	    TypeName tn = desc.getArgumentType(j);
	    popAndTypeCheck(tn, frame_);
	}
	visitInvocationReturnValue(desc);
    }

    public void visit(Instruction.INVOKEINTERFACE i) {
	visit((Instruction.Invocation)i);
    }

    public void visit(Instruction.INVOKE_NATIVE i) {
	int mindex = i.getMethodIndex(buf);
	UnboundSelector.Method sel = invokeNativeList[mindex];
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf) + " (" + sel + ")");
	Descriptor.Method desc = sel.getDescriptor();

	int formalArgumentCount = desc.getArgumentCount();
	for(int j = formalArgumentCount - 1; j >= 0; j--) {
	    TypeName tn = desc.getArgumentType(j);
	    popAndTypeCheck(tn, frame_);
	}
	visitInvocationReturnValue(desc);
    }

    public void visit(Instruction.GETFIELD i) {
	Selector.Field sel = i.getSelector(buf, cp);
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf) + " (" + sel + ")");
	Descriptor.Field desc = sel.getDescriptor();
	TypeName typeName = desc.getType();
	char tag = typeName.getTypeTag();
	SimpleJITReference receiver = typeCheckReference(frame_.pop());
	Blueprint bp = null;
	if (receiver.isNull()) {
	    if (LOG_ENABLED) log("Found a null pointer exception case");
	}
	bp = typeName.isCompound()
	    ? blueprintFor(targetDomain, typeName.asCompound())
	    : null;
	switch(tag) {
	case TypeCodes.BYTE:
	case TypeCodes.BOOLEAN:
	case TypeCodes.CHAR:
	case TypeCodes.SHORT:
	case TypeCodes.INT:
	    frame_.push(new SimpleJITInt());
	    break;
	case TypeCodes.LONG:
	    frame_.pushWide(new SimpleJITLong());
	    break;
	case TypeCodes.FLOAT:
	    frame_.push(new SimpleJITFloat());
	    break;
	case TypeCodes.DOUBLE:
	    frame_.pushWide(new SimpleJITDouble());
	    break;
	case TypeCodes.OBJECT:
	    if (bp != null)
		frame_.push(new SimpleJITReference(bp));
	    else
		frame_.push(new SimpleJITReference(typeName.asCompound()));
	    break;
	case TypeCodes.ARRAY:
	    if (bp != null)
		frame_.push(new SimpleJITArray(bp.asArray()));
	    else
		frame_.push(new SimpleJITArray(typeName.asArray()));
	    break;
	default:
	    throw new OVMError("Unexpected type tag : " + tag);
	}
    }

    public void visit(Instruction.PUTFIELD i) {
	Selector.Field sel = i.getSelector(buf, cp);
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf) + " (" + sel + ")");
	Descriptor.Field desc = sel.getDescriptor();
	TypeName typeName  = desc.getType();
	popAndTypeCheck(typeName, frame_);
	SimpleJITReference receiver = typeCheckReference(frame_.pop());
	if (receiver.isNull())
	    if (LOG_ENABLED) log("Found a null pointer exception case");
    }

    public void visit(Instruction.GETSTATIC i) {
	Selector.Field sel = i.getSelector(buf, cp);
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf) + " (" + sel + ")");
	Descriptor.Field desc = sel.getDescriptor();
	TypeName typeName  = desc.getType();
	char tag = typeName.getTypeTag();
	Blueprint bp = null;
	bp = typeName.isCompound()
	    ? blueprintFor(targetDomain, typeName.asCompound())
	    : null;
	switch(tag) {
	case TypeCodes.BYTE:
	case TypeCodes.BOOLEAN:
	case TypeCodes.CHAR:
	case TypeCodes.SHORT:
	case TypeCodes.INT:
	    frame_.push(new SimpleJITInt());
	    break;
	case TypeCodes.LONG:
	    frame_.pushWide(new SimpleJITLong());
	    break;
	case TypeCodes.FLOAT:
	    frame_.push(new SimpleJITFloat());
	    break;
	case TypeCodes.DOUBLE:
	    frame_.pushWide(new SimpleJITDouble());
	    break;
	case TypeCodes.OBJECT:
	    if (bp != null)
		frame_.push(new SimpleJITReference(bp));
	    else
		frame_.push(new SimpleJITReference(typeName.asCompound()));
	    break;
	case TypeCodes.ARRAY:
	    if (bp != null)
		frame_.push(new SimpleJITArray(bp.asArray()));
	    else
		frame_.push(new SimpleJITArray(typeName.asArray()));
	    break;
	default:
	    throw new OVMError("Unexpected type tag : " + tag);
	}
    }

    public void visit(Instruction.PUTSTATIC i) {
	Selector.Field sel = i.getSelector(buf, cp);
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf) + " (" + sel + ")");
	Descriptor.Field desc = sel.getDescriptor();
	TypeName typeName  = desc.getType();
	popAndTypeCheck(typeName, frame_);
    }

    public void visit(Instruction.SINGLEANEWARRAY i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf));
	int cpindex = i.getCPIndex(buf);
	SimpleJITInt dimension = typeCheckInt(frame_.pop());
	TypeName.Array atn = cp.getClassAt(cpindex)
	    .asTypeName().asArray();
	Blueprint bp = blueprintFor(targetDomain, atn);
	if (dimension.isConstant()) {
	    int dim = dimension.getConstantInt();
	    if (bp != null)
		frame_.push(new SimpleJITArray(bp.asArray(), dim));
	    else
		frame_.push(new SimpleJITArray(atn, dim));
	} else {
	    if (bp != null)
		frame_.push(new SimpleJITArray(bp.asArray()));
	    else
		frame_.push(new SimpleJITArray(atn));
	}
    }

    public void visit(Instruction.ANEWARRAY i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf));
	int cpindex = i.getCPIndex(buf);
	SimpleJITInt dimension = typeCheckInt(frame_.pop());
	TypeName.Compound tn = cp.getClassAt(cpindex).asTypeName().asCompound();
	TypeName.Array atn = TypeName.Array.make(tn, 1);
	Blueprint bp = blueprintFor(targetDomain, atn);
	if (dimension.isConstant()) {
	    int dim = dimension.getConstantInt();
	    if (bp != null)
		frame_.push(new SimpleJITArray(bp.asArray(), dim));
	    else
		frame_.push(new SimpleJITArray(atn, dim));
	} else {
	    if (bp != null)
		frame_.push(new SimpleJITArray(bp.asArray()));
	    else
		frame_.push(new SimpleJITArray(atn));
	}
    }

    public void visit(Instruction.NEWARRAY i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf));
	SimpleJITInt dimension = typeCheckInt(frame_.pop());
	TypeName.Compound tn = i.getArrayName(buf);
	Blueprint bp = blueprintFor(targetDomain, tn);
	if (dimension.isConstant()) {
	    int dim = dimension.getConstantInt();
	    if (bp != null)
		frame_.push(new SimpleJITArray(bp.asArray(), dim));
	    else
		frame_.push(new SimpleJITArray(tn.asArray(), dim));
	} else {
	    if (bp != null)
		frame_.push(new SimpleJITArray(bp.asArray()));
	    else
		frame_.push(new SimpleJITArray(tn.asArray()));
	}
    }

    public void visit(Instruction.MULTIANEWARRAY i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf));
	int dimensions = i.getDimensions(buf);
	TypeName.Array arrayTypeName = i.getClassName(buf, cp);
	Blueprint bp = blueprintFor(targetDomain, arrayTypeName);
	TypeName itn = RepositoryUtils.makeTypeName("I");
	for(int j = 0; j < dimensions; j++) {
	    popAndTypeCheck(itn, frame_);
	}
	if (bp != null)
	    frame_.push(new SimpleJITArray(bp.asArray()));
	else
	    frame_.push(new SimpleJITArray(arrayTypeName));
    }

    public void visit(Instruction.CHECKCAST i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf));
	int cpindex = i.getCPIndex(buf);
	TypeName.Compound tn = cp.getClassAt(cpindex).asTypeName().asCompound();
	SimpleJITReference rec = typeCheckReference(frame_.pop());
	Blueprint pbp = blueprintFor(targetDomain, tn);
	if (pbp != null && rec.resolved()) {
	    Blueprint cbp = rec.getBlueprint();
	    if (cbp.getType().isSubtypeOf(pbp.getType()))
		if (LOG_ENABLED) log("found an useless cast");
	}
	if (tn.isArray())
	    if (pbp != null)
		frame_.push(new SimpleJITArray(pbp.asArray()));
	    else
		frame_.push(new SimpleJITArray(tn.asArray()));
	else
	    if (pbp != null)
		frame_.push(new SimpleJITReference(pbp));
	    else
		frame_.push(new SimpleJITReference(tn));
    }

    public void visit(Instruction.INSTANCEOF i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf));
	int cpindex = i.getCPIndex(buf);
	TypeName.Compound tn = cp.getClassAt(cpindex).asTypeName().asCompound();
	SimpleJITReference rec = typeCheckReference(frame_.pop());
	Blueprint pbp = blueprintFor(targetDomain, tn);
	if (pbp != null && rec.resolved()) {
	    Blueprint cbp = rec.getBlueprint();
	    if (cbp.getType().isSubtypeOf(pbp.getType()))
		if (LOG_ENABLED) log("found an useless subtype test");
	}
	frame_.push(new SimpleJITInt());
    }

    public void visit(Instruction.NEW i) {
	if (LOG_ENABLED) log(pc_ + " : NEW");
	int cpindex = i.getCPIndex(buf);
	TypeName.Compound tn = cp.getClassAt(cpindex).asTypeName().asCompound();
	Blueprint bp = blueprintFor(targetDomain, tn);
	if (bp != null) {
	    frame_.push(new SimpleJITReference(bp));
	} else {
	    frame_.push(new SimpleJITReference(tn));
	}
    }

    public void visit(Instruction.NEW_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : NEW_QUICK");

	if (true)
	    throw new NeedsFiatError("NEW_QUICK");

	/*
	int linkset_index = i.getLinkSetIndex(buf);
	S3Blueprint target_bp = declaringTypeBP
	    .getBlueprintForQuickIndex(linkset_index);
	OVMBase.OVMassert(target_bp.getType().isScalar());
	frame_.push(new SimpleJITReference(target_bp));
	*/
    }

    public void visit(Instruction.ANEWARRAY_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : ANEWARRAY_QUICK");

	if (true)
	    throw new NeedsFiatError("ANEWARRAY_QUICK");

	/*
	int linkset_index = i.getLinkSetIndex(buf);
	S3Blueprint target_bp = declaringTypeBP
	    .getBlueprintForQuickIndex(linkset_index);
	OVMBase.OVMassert(target_bp.getType().isArray());
	SimpleJITInt arraylength = typeCheckInt(frame_.pop());
	if (arraylength.isConstant())
	    frame_.push(new SimpleJITArray
		       (target_bp.asArray(), 
			arraylength.getConstantInt()));
	else
	    frame_.push(new SimpleJITArray
		       (target_bp.asArray()));
	*/
    }

    public void visit(Instruction.MULTIANEWARRAY_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString(buf));
	if (true)
	    throw new NeedsFiatError("MULTIANEWARRAY_QUICK");
    }

    public void visit(Instruction.INSTANCEOF_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : INSTANCEOF_QUICK");

	if (true)
	    throw new NeedsFiatError("INSTANCEOF_QUICK");

	/*
	int linkset_index = i.getLinkSetIndex(buf);
	S3Blueprint providerBp = declaringTypeBP
	    .getBlueprintForQuickIndex(linkset_index);
	SimpleJITReference client 
	    = (SimpleJITReference)frame_.pop();
	S3Blueprint clientBP = (S3Blueprint)client.getBlueprint();
	// Can I do if (clientBP.isSubtypeOf(providerBP)) ... ?
	frame_.push(new SimpleJITInt());
	*/
    }

    public void visit(Instruction.CHECKCAST_QUICK i) {
	if (LOG_ENABLED) log(pc_ + " : CHECKCAST_QUICK");

	if (true)
	    throw new NeedsFiatError("CHECKCAST_QUICK");

	/*
	int linkset_index = i.getLinkSetIndex(buf);
	S3Blueprint providerBP = 
	    declaringTypeBP.getBlueprintForQuickIndex(linkset_index);
	SimpleJITReference client = 
	    (SimpleJITReference)frame_.pop();
	S3Blueprint clientBP = (S3Blueprint)client.getBlueprint();
	if (providerBP.getType().isArray())
	    frame_.push(new SimpleJITArray
		       (providerBP.asArray()));
	else
	    frame_.push(new SimpleJITReference(providerBP));
	*/
    }

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

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

    public void visit(Instruction.IRETURN i) {
	if (LOG_ENABLED) log(pc_ + " : IRETURN");
	typeCheckInt(frame_.pop());
    }

    public void visit(Instruction.LRETURN i) {
	if (LOG_ENABLED) log(pc_ + " : LRETURN");
	typeCheckLong(frame_.popWide());
    }

    public void visit(Instruction.FRETURN i) {
	if (LOG_ENABLED) log(pc_ + " : FRETURN");
	typeCheckFloat(frame_.pop());
    }

    public void visit(Instruction.DRETURN i) {
	if (LOG_ENABLED) log(pc_ + " : DRETURN");
	typeCheckDouble(frame_.popWide());
    }

    public void visit(Instruction.ARETURN i) {
	if (LOG_ENABLED) log(pc_ + " : ARETURN");
	typeCheckReference(frame_.pop());
    }

    public void visit(Instruction.IINC i) {
	if (LOG_ENABLED) log(pc_ + " : IINC");
	int index = i.getLocalVariableOffset(buf);
	int delta = i.getValue(buf);
	SimpleJITInt l = (SimpleJITInt)frame_.load(index);
	if (l.isConstant()) {
	    frame_.store
		(index, 
		 new SimpleJITInt(l.getConstantInt() + delta));
	} else {
	    frame_.store(index, new SimpleJITInt());
	}
    }

    public void visit(Instruction.WIDE_IINC i) {
	if (LOG_ENABLED) log(pc_ + " : WIDE_IINC");
	int index = i.getLocalVariableOffset(buf);
	int delta = i.getValue(buf);
	SimpleJITInt l = (SimpleJITInt)frame_.load(index);
	if (l.isConstant()) {
	    frame_.store
		(index, 
		 new SimpleJITInt(l.getConstantInt() + delta));
	} else {
	    frame_.store(index, new SimpleJITInt());
	}
    }

    public void visit(Instruction.TABLESWITCH i) {
	if (LOG_ENABLED) log(pc_ + " : TABLESWITCH");
	typeCheckInt(frame_.pop());
    }

    public void visit(Instruction.LOOKUPSWITCH i) {
	if (LOG_ENABLED) log(pc_ + " : LOOKUPSWITCH");
	typeCheckInt(frame_.pop());
    }

    public void visit(Instruction.MONITORENTER i) {
	if (LOG_ENABLED) log(pc_ + " : MONITORENTER");
	typeCheckReference(frame_.pop());
    }

    public void visit(Instruction.MONITOREXIT i) {
	if (LOG_ENABLED) log(pc_ + " : MONITOREXIT");
	typeCheckReference(frame_.pop());
    }

    public void visit(Instruction.INVOKE_SYSTEM i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString());
	int mindex = i.getMethodIndex(buf);
	int optype = i.getOpType(buf);
	switch(mindex) {
	case JVMConstants.InvokeSystemArguments.BREAKPOINT:
	    typeCheckReference(frame_.pop());
	    break;
	case RUN:
	    typeCheckInt(frame_.pop());
	    typeCheckInt(frame_.pop());
	    break;
	case EMPTY_CSA_CALL:
	    break;
	case GET_CONTEXT: {
	    typeCheckInt(frame_.pop());
	    frame_.push(new SimpleJITReference(contextBP));
	    break;
	}
	case NEW_CONTEXT:
	    typeCheckReference(frame_.pop());
	    frame_.push(new SimpleJITInt());
	    break;
	case DESTROY_NATIVE_CONTEXT:
	    typeCheckInt(frame_.pop());
	    break;
	case GET_ACTIVATION:
	    typeCheckInt(frame_.pop());
	    frame_.push(new SimpleJITInt());
	    break;
	case MAKE_ACTIVATION:
	    typeCheckReference(frame_.pop());
	    typeCheckReference(frame_.pop());
	    typeCheckInt(frame_.pop());
	    frame_.push(new SimpleJITInt());
	    break;
	case CUT_TO_ACTIVATION:
	    typeCheckInt(frame_.pop());
	    typeCheckInt(frame_.pop());
	    break;
	case INVOKE:
	    typeCheckReference(frame_.pop());
	    typeCheckReference(frame_.pop());
	    switch(optype) {
	    case TypeCodes.VOID:
		break;
	    case TypeCodes.OBJECT:
		frame_.push(new SimpleJITReference());
		break;
	    case TypeCodes.FLOAT:
		frame_.push(new SimpleJITFloat());
		break;
	    case TypeCodes.DOUBLE:
		frame_.pushWide(new SimpleJITDouble());
		break;
	    case TypeCodes.LONG:
		frame_.pushWide(new SimpleJITLong());
		break;
	    case TypeCodes.INT:
		frame_.push(new SimpleJITInt());
		break;
	    default:
		throw new OVMError("Unexpected type tag");
	    }
	    break;
	case START_TRACING:
	    frame_.push(new SimpleJITInt());
	    break;
	case STOP_TRACING:
	    break;
	case BEGIN_OVERRIDE_TRACING:
	    break;
	case END_OVERRIDE_TRACING:
	    break;
	case WORD_OP: {
	    switch(optype) {
	    case WordOps.uI2L:
		typeCheckInt(frame_.pop());
		frame_.pushWide(new SimpleJITLong());
		break;
	    case WordOps.sCMP:
		typeCheckInt(frame_.pop());
		typeCheckInt(frame_.pop());
		frame_.push(new SimpleJITInt());
		break;
	    case WordOps.uCMP:
		typeCheckInt(frame_.pop());
		typeCheckInt(frame_.pop());
		frame_.push(new SimpleJITInt());
		break;
	    case WordOps.uLT:
		typeCheckInt(frame_.pop());
		typeCheckInt(frame_.pop());
		frame_.push(new SimpleJITInt());
		break;
	    case WordOps.uLE:
		typeCheckInt(frame_.pop());
		typeCheckInt(frame_.pop());
		frame_.push(new SimpleJITInt());
		break;
	    case WordOps.uGE:
		typeCheckInt(frame_.pop());
		typeCheckInt(frame_.pop());
		frame_.push(new SimpleJITInt());
		break;
	    case WordOps.uGT:
		typeCheckInt(frame_.pop());
		typeCheckInt(frame_.pop());
		frame_.push(new SimpleJITInt());
		break;
	    default:
		throw new OVMError.Internal
		    ("Unsupported invoke_system wordop argument : " + optype);
	    }
	    break;
	}
	case DEREFERENCE: {
	    switch(optype) {
	    case DereferenceOps.getByte:
		typeCheckReference(frame_.pop());
		frame_.push(new SimpleJITInt());
		break;
	    case DereferenceOps.getShort:
		typeCheckReference(frame_.pop());
		frame_.push(new SimpleJITInt());
		break;
	    case DereferenceOps.setByte:
		typeCheckInt(frame_.pop());
		typeCheckReference(frame_.pop());
		break;
	    case DereferenceOps.setShort:
		typeCheckInt(frame_.pop());
		typeCheckReference(frame_.pop());
		break;
	    case DereferenceOps.setBlock:
		typeCheckInt(frame_.pop());
		typeCheckReference(frame_.pop());
		typeCheckReference(frame_.pop());
		break;
	    default:
		throw new OVMError
		    ("Unsupported invoke_system dereference op argument : " + optype);
	    }
	    break;
	}

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

    public void visit(Instruction.ATHROW i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString());
	frame_.pop();
    }

    public void visit(Instruction.POLLCHECK i) {
	if (LOG_ENABLED) log(pc_ + " : " + i.toString());
    }

}

