package s3.services.simplejit.ir;

import ovm.core.repository.RepositoryUtils;
import ovm.core.repository.Selector;
import ovm.core.repository.TypeCodes;
import ovm.core.repository.TypeName;
import ovm.core.repository.UnboundSelector;
import ovm.services.bytecode.JVMConstants;
import ovm.util.OVMError;
import s3.services.simplejit.ir.SimpleJITOperand.SimpleJITBranchOperand;
import s3.services.simplejit.ir.SimpleJITOperand.SimpleJITConditionOperand;
import s3.services.simplejit.ir.SimpleJITOperand.SimpleJITIntOperand;
import s3.services.simplejit.ir.SimpleJITOperand.SimpleJITLocalTemporaryOperand;
import s3.services.simplejit.ir.SimpleJITOperand.SimpleJITTemporaryDefOperand;
import s3.services.simplejit.ir.SimpleJITOperand.SimpleJITTemporaryUseOperand;
import s3.services.simplejit.util.SimpleJITIdentitySet;

public class SimpleJITInstruction
implements JVMConstants, 
           JVMConstants.InvokeSystemArguments, 
           JVMConstants.Opcodes,
           TypeCodes {

    static final int tabWidth = 24;

    static String formatBase(String opcode, String operand1) {
	int w = opcode.length() - tabWidth;
	if (w < 0) {
	    StringBuffer b = new StringBuffer(opcode);
	    for(int i = 0; i < -w; i++) {
		b.append(" ");
	    }
	    return b.append(operand1).toString();
	} else {
	    throw new OVMError
		("SimpleJITInstruction : tab width is too small for " + opcode);
	}
    }

    static String format(String opcode) {
	return formatBase(opcode, "");
    }

    static String format(String opcode, Object operand1) {
	return formatBase(opcode, operand1.toString());
    }

    static String format(String opcode, Object operand1, 
			 Object operand2, Object operand3,
			 Object[] operands) {
	String list = "[ ";
	for(int i = 0; i < operands.length; i++)
	    list += operands[i] + " ";
	if (operand1 == null) {
	    System.err.println("operand 1 null");
	    System.exit(1);
	}
	if (operand2 == null) {
	    System.err.println("operand 2 null");
	    System.exit(1);
	}
	if (operand3 == null) {
	    System.err.println("operand 3 null");
	    System.exit(1);
	}
	return formatBase(opcode, operand1.toString() + " " 
			  + operand2.toString() + " " 
			  + operand3.toString() + " " 
			  + list + "]");
    }

    static String format(String opcode, Object operand1, 
			 Object operand2, Object[] operands3,
			 Object operand4) {
	String list = "[ ";
	for(int i = 0; i < operands3.length; i++)
	    list += operands3[i] + " ";
	if (operand1 == null) {
	    System.err.println("operand 1 null");
	    System.exit(1);
	}
	if (operand2 == null) {
	    System.err.println("operand 2 null");
	    System.exit(1);
	}
	if (operand4 == null) {
	    System.err.println("operand 4 null");
	    System.exit(1);
	}
	return formatBase(opcode, operand1.toString() + " " 
			  + operand2.toString() + " " 
			  + list + "] "
			  + operand4.toString());
    }

    static String format(String opcode, Object operand1, 
			 Object operand2, Object[] operands) {
	String list = "[ ";
	for(int i = 0; i < operands.length; i++)
	    list += operands[i] + " ";
	return formatBase(opcode, operand1.toString() + " " 
			  + operand2.toString() + " " + list + "]");
    }
    static String format(String opcode, Object operand1, 
			 Object[] operands, Object operand2) {
	String list = "[ ";
	for(int i = 0; i < operands.length; i++)
	    list += operands[i] + " ";
	return formatBase(opcode, operand1.toString() + " " 
			  + list + "] " + operand2.toString());
    }

    static String format(String opcode, Object operand1, Object[] operands) {
	String list = "[ ";
	for(int i = 0; i < operands.length; i++)
	    list += operands[i] + " ";
	return formatBase(opcode, operand1.toString() + " " + list + "]");
    }

    static String format(String opcode, Object[] operands, Object operand1) {
	String list = "[ ";
	for(int i = 0; i < operands.length; i++)
	    list += operands[i] + " ";
	return formatBase(opcode, list + "] " + operand1.toString());
    }

    static String format(String opcode, Object[] operands) {
	String list = "[ ";
	for(int i = 0; i < operands.length; i++)
	    list += operands[i] + " ";
	return formatBase(opcode, list + "]");
    }

    static String format(String opcode, Object operand1, Object operand2) {
	return formatBase(opcode, operand1 + " " + operand2);
    }

    static String format(String opcode, Object operand1, 
			 Object operand2, Object operand3) {
	return formatBase(opcode, operand1 + " " + operand2 + " " + operand3);
    }

    static String format(String opcode, 
			 Object operand1, Object operand2, 
			 Object operand3, Object operand4) {
	return formatBase(opcode, 
			  operand1 + " " + operand2 + " " 
			  + operand3 + " " + operand4);
    }

    static String format(String opcode, 
			 Object operand1, Object operand2, 
			 Object operand3, Object operand4,
			 Object operand5) {
	return formatBase(opcode, operand1 + " " + operand2 + " " 
			  + operand3 + " " + operand4 + " " + operand5);
    }




    /**
     * A marker for an instruction that writes result operand(s)
     */
    public interface Definer {
	SimpleJITOperand[] getDefOperands();
	void setDefOperands(SimpleJITOperand[] defs);
    }

    /**
     * A marker for an instruction that reads use operand(s)
     */
    public interface User {
	SimpleJITOperand[] getUseOperands();
	void setUseOperands(SimpleJITOperand[] uses);
    }

    public interface Brancher {
	SimpleJITBranchOperand[] getBranchOperands();
	void setBranchOperands(SimpleJITBranchOperand[] branches);
    }

    /**
     * A marker for an Definer instruction that cannot be deleted
     * (dead) for some reason (eg exception) even though its results
     * are not used.
     */
    public interface SideEffector {
    }

    public interface ConstantPoolReferer {
	int getCPIndex();
	void setCPIndex(int cpindex);
    }

    private static int IDCounter = 0;
    private int ID;

    /** A set of SimpleJITTemporaryDefOperands reaching into this
     * instruction */
    protected SimpleJITIdentitySet reaching_in_defs;
    /** A set of SimpleJITTemporaryDefOperands reaching out of this
     * instruction */
    protected SimpleJITIdentitySet reaching_out_defs;

    /** A set of SimpleJITTemporaryUseOperands that are live at the
     * start of the instruction */
    protected SimpleJITIdentitySet live_in_uses;
    /** A set of SimpleJITTemporaryUseOperands that are live at the
     * end of the instruction */
    protected SimpleJITIdentitySet live_out_uses;

    public SimpleJITInstruction() {
	ID = IDCounter++;
	reaching_in_defs = new SimpleJITIdentitySet();
	reaching_out_defs = new SimpleJITIdentitySet();
	live_in_uses = new SimpleJITIdentitySet();
	live_out_uses = new SimpleJITIdentitySet();
    }

    public void setReachingInDefs(SimpleJITIdentitySet s) {
	reaching_in_defs = s.copy();
    }
    public void setReachingOutDefs(SimpleJITIdentitySet s) {
	reaching_out_defs = s.copy();
    }
    public SimpleJITIdentitySet getReachingInDefs() {
	return reaching_in_defs.copy();
    }
    public SimpleJITIdentitySet getReachingOutDefs() {
	return reaching_out_defs.copy();
    }
    public void addReachingInDefs(SimpleJITTemporaryDefOperand d) {
	reaching_in_defs.add(d);
    }
    public void addReachingOutDefs(SimpleJITTemporaryDefOperand d) {
	reaching_out_defs.add(d);
    }
    public SimpleJITTemporaryDefOperand[] getReachingInDefsAsArray() {
	SimpleJITTemporaryDefOperand[] ret =
	    new SimpleJITTemporaryDefOperand[reaching_in_defs.size()];
	reaching_in_defs.toArray(ret);
	return ret;
    }
    public SimpleJITTemporaryDefOperand[] getReachingOutDefsAsArray() {
	SimpleJITTemporaryDefOperand[] ret =
	    new SimpleJITTemporaryDefOperand[reaching_out_defs.size()];
	reaching_out_defs.toArray(ret);
	return ret;
    }

    public void setLiveInUses(SimpleJITIdentitySet s) {
	live_in_uses = s.copy();
    }
    public void setLiveOutUses(SimpleJITIdentitySet s) {
	live_out_uses = s.copy();
    }
    public SimpleJITIdentitySet getLiveInUses() {
	return live_in_uses.copy();
    }
    public SimpleJITIdentitySet getLiveOutUses() {
	return live_out_uses.copy();
    }
    public void addLiveInUses(SimpleJITTemporaryUseOperand u) {
	live_in_uses.add(u);
    }
    public void addLiveOutUses(SimpleJITTemporaryUseOperand u) {
	live_out_uses.add(u);
    }
    public SimpleJITTemporaryUseOperand[] getLiveInUsesAsArray() {
	SimpleJITTemporaryUseOperand[] ret =
	    new SimpleJITTemporaryUseOperand[live_in_uses.size()];
	live_in_uses.toArray(ret);
	return ret;
    }
    public SimpleJITTemporaryUseOperand[] getLiveOutUsesAsArray() {
	SimpleJITTemporaryUseOperand[] ret =
	    new SimpleJITTemporaryUseOperand[live_out_uses.size()];
	live_out_uses.toArray(ret);
	return ret;
    }

    public boolean isConstantPoolReferer() {
	return this instanceof ConstantPoolReferer;
    }
    public ConstantPoolReferer asConstantPoolReferer() {
	return (ConstantPoolReferer)this;
    }
    public boolean isDefiner() {
	return this instanceof Definer;
    }
    public boolean isUser() {
	return this instanceof User;
    }
    public Definer asDefiner() {
	return (Definer)this;
    }
    public User asUser() {
	return (User)this;
    }
    public boolean isSideEffector() {
	return this instanceof SideEffector;
    }
    public SideEffector asSideEffector() {
	return (SideEffector)this;
    }

    public boolean isBrancher() {
	return this instanceof Brancher;
    }
    public Brancher asBrancher() {
	return (Brancher)this;
    }

    /**
     * Every block should have at least one instruction. This
     * instruction is used for blocks with no real instruction.
     */
    public static class Dummy 
	extends SimpleJITInstruction {
	public Dummy() {
	}
	public String toString() {
	    return format("Dummy");
	}
    }

    public static class NullCheck 
	extends SimpleJITInstruction 
	implements Definer, User, SideEffector {
	SimpleJITOperand nonnull_obj;
	SimpleJITOperand obj;
	public NullCheck(SimpleJITOperand no,
			 SimpleJITOperand o) {
	    nonnull_obj = no;
	    obj = o;
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { nonnull_obj };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    nonnull_obj = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { obj };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    obj = uses[0];
	}
    }

    public static class ArrayBoundCheck
	extends SimpleJITInstruction 
	implements Definer, User, SideEffector {
	SimpleJITOperand checked_obj;
	SimpleJITOperand checked_index;
	SimpleJITOperand obj;
	SimpleJITOperand index;
	public ArrayBoundCheck(SimpleJITOperand co,
			       SimpleJITOperand ci,
			       SimpleJITOperand o,
			       SimpleJITOperand i) {
	    checked_obj = co;
	    checked_index = ci;
	    obj = o;
	    index = i;
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { checked_obj, checked_index };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    checked_obj = r[0];
	    checked_index = r[1];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { obj, index };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 2)
		throw new Error();
	    obj = uses[0];
	    index = uses[1];
	}
    }

    public static class ArrayStoreCheck
	extends SimpleJITInstruction 
	implements User {
	SimpleJITOperand array;
	SimpleJITOperand elem;
	public ArrayStoreCheck(SimpleJITOperand a,
			       SimpleJITOperand e) {
	    array = a;
	    elem = e;
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { array, elem };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 2)
		throw new Error();
	    array = uses[0];
	    elem = uses[1];
	}
    }

    public static class DivisionByZeroCheck
	extends SimpleJITInstruction 
	implements User {
	SimpleJITOperand array;
	SimpleJITOperand elem;
	public DivisionByZeroCheck(SimpleJITOperand a,
				   SimpleJITOperand e) {
	    array = a;
	    elem = e;
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { array, elem };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 2)
		throw new Error();
	    array = uses[0];
	    elem = uses[1];
	}
    }

    public static class GetThrowable
	extends SimpleJITInstruction 
	implements Definer {
	SimpleJITOperand dest;
	public GetThrowable(SimpleJITOperand dest) {
	    this.dest = dest;
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { dest };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    dest = r[0];
	}
	public String toString() {
	    return format("GetThrowable", dest);
	}
    }

    public static class GetArg
	extends SimpleJITInstruction 
	implements Definer {
	SimpleJITOperand dest;
	char type;
	int index;
	public GetArg(char type, SimpleJITOperand dest, int index) {
	    this.dest = dest;
	    this.type = type;
	    this.index = index;
	}
	public char getType() {
	    return type;
	}
	public int getIndex() { 
	    return index;
	}
	public SimpleJITOperand getDestination() {
	    return dest;
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { dest };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    dest = r[0];
	}
	public String toString() {
	    return format(type + "GetArg" + index, dest);
	}
    }

    public static class Move 
	extends SimpleJITInstruction 
	implements Definer, User {
	SimpleJITOperand dest;
	SimpleJITOperand src;
	protected Move(SimpleJITOperand dest,
		       SimpleJITOperand src) {
	    this.dest = dest;
	    this.src = src;
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { dest };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    dest = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { src };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    src = uses[0];
	}
	public static Move create(char type, 
				  SimpleJITOperand dest,
				  SimpleJITOperand src,
				  TypeName typeName) {
	    // Todo : type assertion on operands
	    switch(type) {
	    case INT:
		return new IntMove(dest, src);
	    case LONG:
		return new LongMove(dest, src);
	    case FLOAT:
		return new FloatMove(dest, src);
	    case DOUBLE:
		return new DoubleMove(dest, src);
	    case OBJECT:
	    case ARRAY:
	    case REFERENCE:
		return new RefMove(typeName, dest, src);
	    default:
		throw new OVMError("Unexpected type code : " + type);
	    }
	}
    }

    public static class IntMove extends Move {
	public IntMove(SimpleJITOperand dest,
		       SimpleJITOperand src) {
	    super(dest, src);
	}
	public String toString() {
	    return format("IntMove", dest, src);
	}
    }

    public static class FloatMove extends Move {
	public FloatMove(SimpleJITOperand dest,
			 SimpleJITOperand src) {
	    super(dest, src);
	}
	public String toString() {
	    return format("FloatMove", dest, src);
	}
    }

    public static class LongMove extends Move {
	public LongMove(SimpleJITOperand dest,
			SimpleJITOperand src) {
	    super(dest, src);
	}
	public String toString() {
	    return format("LongMove", dest, src);
	}
    }

    public static class DoubleMove extends Move {
	public DoubleMove(SimpleJITOperand dest,
			  SimpleJITOperand src) {
	    super(dest, src);
	}
	public String toString() {
	    return format("DoubleMove", dest, src);
	}
    }

    public static class RefMove extends Move {
	TypeName tn;
	public RefMove(TypeName tn,
		       SimpleJITOperand dest,
		       SimpleJITOperand src) {
	    super(dest, src);
	    this.tn = tn;
	}
	public String toString() {
	    return format("RefMove", dest, src);
	}
    }

    public static class ConstantLoad
	extends SimpleJITInstruction
	implements Definer, ConstantPoolReferer {
	SimpleJITOperand dest;
	int cpindex;
	byte cptag;
	protected ConstantLoad(SimpleJITOperand dest,
			       int cpindex,
			       byte cptag) {
	    this.dest = dest;
	    this.cpindex = cpindex;
	    this.cptag = cptag;
	}
	public int getCPIndex() {
	    return cpindex;
	}
	public void setCPIndex(int cpi) {
	    cpindex = cpi;
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { dest };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    dest = r[0];
	}
	public String toString() {
	    return format
		("ConstantLoad", dest, 
		 "[CP," + cpindex + ","
		 + SimpleJITOperand.cptag2name[cptag] + "]");
	}
    }

    public static class TableSwitch 
	extends SimpleJITInstruction 
	implements User, Brancher {
	SimpleJITOperand value;
	int low;
	int high;
	SimpleJITBranchOperand defBlock;
	SimpleJITBranchOperand[] targetBlocks;
	int[] absoluteTargets;
	public TableSwitch(SimpleJITOperand value,
			   int low,
			   int high,
			   SimpleJITBasicBlock defBlock,
			   SimpleJITBasicBlock[] targetBlocks) {
	    this.value = value;
	    this.low = low;
	    this.high = high;
	    this.defBlock = new SimpleJITBranchOperand(defBlock);
	    this.targetBlocks 
		= SimpleJITBranchOperand.blocks2BranchOperands(targetBlocks);
	}

	public SimpleJITBranchOperand[] getBranchOperands() {
	    SimpleJITBranchOperand[] branches = 
		new SimpleJITBranchOperand[1 + targetBlocks.length];
	    for(int i = 0; i < targetBlocks.length; i++)
		branches[i] = targetBlocks[i];
	    branches[branches.length - 1] = defBlock;
	    return branches;
	}
	public void setBranchOperands(SimpleJITBranchOperand[] branches) {
	    if (branches.length != 1 + targetBlocks.length)
		throw new Error();
	    for(int i = 0; i < targetBlocks.length; i++)
		targetBlocks[i] = branches[i];
	    defBlock = branches[branches.length - 1];
	}

	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { value };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    value = uses[0];
	}
	public String toString() {
	    return format("TableSwitch", 
			  value, 
			  new SimpleJITIntOperand(low), 
			  new SimpleJITIntOperand(high), 
			  defBlock,
			  targetBlocks);
	}
    }

    public static class LookupSwitch 
	extends SimpleJITInstruction 
	implements User, Brancher {
	SimpleJITOperand value;
	int npairs;
	int[] cases;
	SimpleJITBranchOperand defBlock;
	SimpleJITBranchOperand[] targetBlocks;
	public LookupSwitch(SimpleJITOperand value,
			    int npairs,
			    int[] cases,
			    SimpleJITBasicBlock defBlock,
			    SimpleJITBasicBlock[] targetBlocks) {
	    this.value = value;
	    this.npairs = npairs;
	    this.cases = cases;
	    this.defBlock = new SimpleJITBranchOperand(defBlock);
	    this.targetBlocks 
		= SimpleJITBranchOperand.blocks2BranchOperands(targetBlocks);
	}

	public SimpleJITBranchOperand[] getBranchOperands() {
	    SimpleJITBranchOperand[] branches = 
		new SimpleJITBranchOperand[1 + targetBlocks.length];
	    for(int i = 0; i < targetBlocks.length; i++)
		branches[i] = targetBlocks[i];
	    branches[branches.length - 1] = defBlock;
	    return branches;
	}
	public void setBranchOperands(SimpleJITBranchOperand[] branches) {
	    if (branches.length != 1 + targetBlocks.length)
		throw new Error();
	    for(int i = 0; i < targetBlocks.length; i++)
		targetBlocks[i] = branches[i];
	    defBlock = branches[branches.length - 1];
	}

	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { value };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    value = uses[0];
	}
	public String toString() {
	    return format("LookupSwitch", value, new SimpleJITIntOperand(npairs), 
			  cases, defBlock, targetBlocks);
	}
    }

    public static class Ret
	extends SimpleJITInstruction
	implements User {
	SimpleJITOperand returnAddressLocal;
	public Ret(SimpleJITLocalTemporaryOperand l) {
	    returnAddressLocal = l;
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { returnAddressLocal };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    returnAddressLocal = uses[0];
	}
	public String toString() {
	    return format("Ret", returnAddressLocal);
	}
    }

    public static class Goto 
	extends SimpleJITInstruction 
	implements Brancher {
	SimpleJITBranchOperand targetBlock;
	public Goto(SimpleJITBasicBlock targetBlock) {
	    this.targetBlock = new SimpleJITBranchOperand(targetBlock);
	}
	public SimpleJITBranchOperand[] getBranchOperands() {
	    return new SimpleJITBranchOperand[] { targetBlock };
	}
	public void setBranchOperands(SimpleJITBranchOperand[] branches) {
	    if (branches.length != 1)
		throw new Error();
	    targetBlock = branches[0];
	}

	public String toString() {
	    return format("Goto", targetBlock);
	}
    }

    public static class Jsr 
	extends SimpleJITInstruction
	implements Brancher {
	SimpleJITBranchOperand targetBlock;
	public Jsr(SimpleJITBasicBlock targetBlock) {
	    this.targetBlock = new SimpleJITBranchOperand(targetBlock);
	}
	public SimpleJITBranchOperand[] getBranchOperands() {
	    return new SimpleJITBranchOperand[] { targetBlock };
	}
	public void setBranchOperands(SimpleJITBranchOperand[] branches) {
	    if (branches.length != 1)
		throw new Error();
	    targetBlock = branches[0];
	}
	public String toString() {
	    return format("Jsr", targetBlock);
	}
    }

    public static class IfCmp 
	extends SimpleJITInstruction 
	implements User, Brancher {
	char type;
	SimpleJITConditionOperand condition;
	SimpleJITOperand op1;
	SimpleJITOperand op2;
	SimpleJITBranchOperand targetBlock;
	SimpleJITBranchOperand fallThoughBlock;

	public IfCmp(char type,
		     SimpleJITConditionOperand condition,
		     SimpleJITOperand op1,
		     SimpleJITOperand op2,
		     SimpleJITBasicBlock targetBlock,
		     SimpleJITBasicBlock fallThoughBlock) {
	    this.type = type;
	    this.condition = condition;
	    this.op1 = op1;
	    this.op2 = op2;
	    this.targetBlock = new SimpleJITBranchOperand(targetBlock);
	    this.fallThoughBlock = new SimpleJITBranchOperand(fallThoughBlock);
	}
	public SimpleJITBranchOperand[] getBranchOperands() {
	    return new SimpleJITBranchOperand[] 
		{ targetBlock, fallThoughBlock };
	}
	public void setBranchOperands(SimpleJITBranchOperand[] branches) {
	    if (branches.length != 2)
		throw new Error();
	    targetBlock = branches[0];
	    fallThoughBlock = branches[1];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { op1, op2 };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 2)
		throw new Error();
	    op1 = uses[0];
	    op2 = uses[1];
	}
	public String toString() {
	    return format("IfCmp_" + type, condition, op1, op2, 
			  targetBlock, fallThoughBlock);
	}
    }

    public static class SimpleOp 
	extends SimpleJITInstruction 
	implements Definer, User {
	int operator;
	SimpleJITOperand result;
	SimpleJITOperand op1;
	protected SimpleOp(int operator,
			   SimpleJITOperand result,
			   SimpleJITOperand op1) {
	    this.operator = operator;
	    this.result = result;
	    this.op1 = op1;
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { op1 };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    op1 = uses[0];
	}
    }

    public static class UnaryOp extends SimpleOp {
	public UnaryOp(int operator,
		       SimpleJITOperand result,
		       SimpleJITOperand op1) {
	    super(operator, result, op1);
	}
	public String toString() {
	    return format(operatorNames[operator], result, op1);
	}
    }

    public static class BinaryOp extends SimpleOp {
	SimpleJITOperand op2;
	public BinaryOp(int operator,
			SimpleJITOperand result,
			SimpleJITOperand op1,
			SimpleJITOperand op2) {
	    super(operator, result, op1);
	    this.op2 = op2;
	}
	public String toString() {
	    return format(operatorNames[operator], result, op1, op2);
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { op1, op2 };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 2)
		throw new Error();
	    op1 = uses[0];
	    op2 = uses[1];
	}
    }

    public static class ArrayAccess 
	extends SimpleJITInstruction {
	char type;
	SimpleJITOperand array;
	SimpleJITOperand index;
	protected ArrayAccess(char type,
			      SimpleJITOperand array,
			      SimpleJITOperand index) {
	    this.type = type;
	    this.array = array;
	    this.index = index;
	}
    }

    public static class ArrayLoad 
	extends ArrayAccess 
	implements Definer, User {
	SimpleJITOperand result;
	public ArrayLoad(char type,
			 SimpleJITOperand result,
			 SimpleJITOperand array,
			 SimpleJITOperand index) {
	    super(type, array, index);
	    this.result = result;
	}
	public String toString() {
	    return format(type + "ArrayLoad", result, array, index);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { array, index };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 2)
		throw new Error();
	    array = uses[0];
	    index = uses[1];
	}
    }

    public static class ArrayStore 
	extends ArrayAccess 
	implements User {
	SimpleJITOperand value;
	public ArrayStore(char type,
			  SimpleJITOperand array,
			  SimpleJITOperand index,
			  SimpleJITOperand value) {
	    super(type, array, index);
	    this.value = value;
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { array, index, value };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 3)
		throw new Error();
	    array = uses[0];
	    index = uses[1];
	    value = uses[2];
	}
	public String toString() {
	    return format(type + "ArrayStore", array, index, value);
	}
    }

    public static class RefArrayStore extends ArrayStore {
	public RefArrayStore(SimpleJITOperand array,
			     SimpleJITOperand index,
			     SimpleJITOperand value) {
	    super(REFERENCE, array, index, value);
	}
	public String toString() {
	    return format("AArrayStoreRef", array, index, value);
	}
    }

    public static class UncheckedRefArrayStore extends RefArrayStore {
	public UncheckedRefArrayStore(SimpleJITOperand array,
				      SimpleJITOperand index,
				      SimpleJITOperand value) {
	    super(array, index, value);
	}
	public String toString() {
	    return format("UncheckedAArrayStore", array, index, value);
	}
    }

    public static class RefArrayStoreWithBarrier extends RefArrayStore {
 	public RefArrayStoreWithBarrier(SimpleJITOperand array,
 					SimpleJITOperand index,
 					SimpleJITOperand value) {
 	    super(array, index, value);
 	}
 	public String toString() {
 	    return format("AArrayStoreBarrier", array, index, value);
 	}
    }

    public static class RefArrayLoad extends ArrayLoad {
 	public RefArrayLoad(SimpleJITOperand result,
		 SimpleJITOperand array,
		 SimpleJITOperand index) {
 	    super(REFERENCE, result, array, index);
 	}
 	public String toString() {
 	    return format("AArrayLoadRef", result, array, index);
 	}
    }

    public static class ArrayLength 
	extends UnaryOp 
	implements Definer, User {
	public ArrayLength(SimpleJITOperand result,
			   SimpleJITOperand array) {
	    super(Operators.ARRAYLENGTH, result, array);
	}
	public String toString() {
	    return format("ArrayLength", result, op1);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { op1 };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    op1 = uses[0];
	}
    }

    public static class CallVoid extends Call {
	public CallVoid(int operator,
			SimpleJITOperand[] args,
			Selector.Method sel,
			int cpindex) {
	    super(operator, args, sel, cpindex);
	}
	public CallVoid(int operator,
			SimpleJITOperand[] args,
			Selector.Method sel) {
	    super(operator, args, sel);
	}
	public String toString() {
	    String operator = operatorAsString();
	    return format("CallVoid", operator, args, sel);
	}
    }
    public static class CallNonVoid 
	extends Call 
	implements Definer {
	SimpleJITOperand result;
	public CallNonVoid(int operator,
			   SimpleJITOperand result,
			   SimpleJITOperand[] args,
			   Selector.Method sel,
			   int cpindex) {
	    super(operator, args, sel, cpindex);
	    this.result = result;
	}
	public CallNonVoid(int operator,
			   SimpleJITOperand result,
			   SimpleJITOperand[] args,
			   Selector.Method sel) {
	    super(operator, args, sel);
	    this.result = result;
	}
	public SimpleJITOperand getResult() {
	    return result;
	}
	public String toString() {
	    String operator = operatorAsString();
	    return format("CallNonVoid", operator, result, args, sel);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
    }
    public static class Call 
	extends SimpleJITInstruction 
	implements User, SideEffector, ConstantPoolReferer {
	int operator;
	SimpleJITOperand[] args;
	Selector.Method sel;
	int cpindex;
	protected Call(int operator,
		       SimpleJITOperand[] args,
		       Selector.Method sel,
		       int cpindex) {
	    this.operator = operator;
	    this.sel = sel;
	    this.args = args;
	    this.cpindex = cpindex;
	}
	protected Call(int operator,
		       SimpleJITOperand[] args,
		       Selector.Method sel) {
	    this.operator = operator;
	    this.sel = sel;
	    this.args = args;
	    this.cpindex = -1;
	}
	public int getCPIndex() {
	    return cpindex;
	}
	public void setCPIndex(int cpi) {
	    cpindex = cpi;
	}
	public Selector.Method getSelector() {
	    return sel;
	}
	public SimpleJITOperand[] getArgs() {
	    return (SimpleJITOperand[])args.clone();
	}
	public SimpleJITOperand[] getUseOperands() {
	    return (SimpleJITOperand[])args.clone();
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != args.length)
		throw new Error();
	    args = (SimpleJITOperand[])uses.clone();
	}
	
	public int getOperator() {
	    return operator;
	}

	protected String operatorAsString() {
	    String suffix = null;
	    switch(operator) {
	    case Operators.INVOKEVIRTUAL:
		suffix = "VR";
		break;
	    case Operators.INVOKESPECIAL:
		suffix = "SP";
		break;
	    case Operators.INVOKESTATIC:
		suffix = "ST";
		break;
	    case Operators.INVOKEINTERFACE:
		suffix = "IF";
		break;
	    case Operators.INVOKE_SYSTEM:
		suffix = "SYS";
		break;
	    case Operators.INVOKE_NATIVE:
		suffix = "NAT";
		break;
	    default:
		throw new OVMError("Unexpected operator : " + operatorNames[operator]);
	    }
	    return suffix;
	}
    }

    public static class NativeCallVoid 
	extends NativeCall {
	public NativeCallVoid(int nativeCallNumber,
			      SimpleJITOperand[] args,
			      UnboundSelector.Method sel) {
	    super(nativeCallNumber, 
		  args,
		  sel);
	}
	public String toString() {
	    return format("NativeCallVoid", 
			  Integer.toString(nativeCallNumber), 
			  args,
			  sel);
	}
    }

    public static class NativeCallNonVoid 
	extends NativeCall 
	implements Definer {
	SimpleJITOperand result;
	public NativeCallNonVoid(int nativeCallNumber,
				 SimpleJITOperand result,
				 SimpleJITOperand[] args,
				 UnboundSelector.Method sel) {
	    super(nativeCallNumber, 
		  args,
		  sel);
	    this.result = result;
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
	public String toString() {
	    return format("NativeCallNonVoid", 
			  Integer.toString(nativeCallNumber), 
			  result, 
			  args,
			  sel);
	}
    }

    static final TypeName.Scalar nativeTN =
        RepositoryUtils.makeTypeName("ovm/core/execution","Native").asScalar();
        
    public static class NativeCall extends Call {
	int nativeCallNumber;
	protected NativeCall(int nativeCallNumber,
			     SimpleJITOperand[] args,
			     UnboundSelector.Method sel) {
	    super(Operators.INVOKE_NATIVE, args, 
                  Selector.Method.make(sel, nativeTN));
	    this.nativeCallNumber = nativeCallNumber;
	}
    }


    public static class SystemCallVoid 
	extends SystemCall {
	public SystemCallVoid(int systemCallNumber,
			      int optype,
			      SimpleJITOperand[] args) {
	    super(systemCallNumber, optype, args);
	}
	public String toString() {
	    return format("SystemCallVoid", 
			  Integer.toString(systemCallNumber), 
			  Integer.toString(optype), 
			  args);
	}
    }
    public static class SystemCallNonVoid
	extends SystemCall 
	implements Definer {
	SimpleJITOperand result;
	public SystemCallNonVoid(int systemCallNumber,
				 int optype,
				 SimpleJITOperand result,
				 SimpleJITOperand[] args) {
	    super(systemCallNumber, optype, args);
	    this.result = result;
	}
	public String toString() {
	    return format("SystemCallNonVoid", 
			  Integer.toString(systemCallNumber), 
			  Integer.toString(optype), 
			  result, 
			  args);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
    }
    public static class SystemCall extends Call {
	int systemCallNumber;
	int optype;
	protected SystemCall(int systemCallNumber,
			     int optype,
			     SimpleJITOperand[] args) {
	    super(Operators.INVOKE_SYSTEM, args, null);
	    this.systemCallNumber = systemCallNumber;
	    this.optype = optype;
	}
    }

    public static class FieldAccess 
	extends SimpleJITInstruction
	implements ConstantPoolReferer {
	Selector.Field sel;
	int cpindex;
	protected FieldAccess(Selector.Field sel, int cpindex) {
	    this.sel = sel;
	}
	public int getCPIndex() {
	    return cpindex;
	}
	public void setCPIndex(int cpi) {
	    cpindex = cpi;
	}
    }

    public static class GetField 
	extends FieldAccess 
	implements Definer, User {
	SimpleJITOperand result;
	SimpleJITOperand obj;
	public GetField(SimpleJITOperand result,
			SimpleJITOperand obj,
			Selector.Field sel,
			int cpindex) {
	    super(sel, cpindex);
	    this.result = result;
	    this.obj = obj;
	}
	public String toString() {
	    return format("GetField", result, obj, sel);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { obj };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    obj = uses[0];
	}
    }

    public static class PutField 
	extends FieldAccess 
	implements User {
	SimpleJITOperand value;
	SimpleJITOperand obj;
	public PutField(SimpleJITOperand obj,
			SimpleJITOperand value,
			Selector.Field sel,
			int cpindex) {
	    super(sel, cpindex);
	    this.value = value;
	    this.obj = obj;
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { obj, value };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 2)
		throw new Error();
	    obj = uses[0];
	    value = uses[1];
	}
	public String toString() {
	    return format("PutField", obj, value, sel);
	}
    }

    public static class PutFieldWithBarrier extends PutField {
	public PutFieldWithBarrier(SimpleJITOperand obj,
				   SimpleJITOperand value,
				   Selector.Field sel,
				   int cpindex) {
	    super(obj, value, sel, cpindex);
	}
	public String toString() {
	    return format("PutFieldBarrier", obj, value, sel);
	}
    }

    public static class ReadBarrier extends SimpleJITInstruction {
     SimpleJITOperand val;
     public ReadBarrier(SimpleJITOperand val) {
 	    this.val=val;
 	}
 	public String toString() {
 	    return format("ReadBarrier", val);
 	}
    }

    public static class GetStatic
	extends FieldAccess 
	implements Definer {
	SimpleJITOperand result;
	public GetStatic(SimpleJITOperand result,
			 Selector.Field sel,
			 int cpindex) {
	    super(sel, cpindex);
	    this.result = result;
	}
	public String toString() {
	    return format("GetStatic", result, sel);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
    }

    public static class PutStatic
	extends FieldAccess 
	implements User {
	SimpleJITOperand value;
	public PutStatic(SimpleJITOperand value,
			 Selector.Field sel,
			 int cpindex) {
	    super(sel, cpindex);
	    this.value = value;
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { value };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    value = uses[0];
	}
	public String toString() {
	    return format("PutStatic", value, sel);
	}
    }

    public static class PutStaticWithBarrier
	extends PutStatic {
	public PutStaticWithBarrier(SimpleJITOperand value,
				    Selector.Field sel,
				    int cpindex) {
	    super(value, sel, cpindex);
	}
	public String toString() {
	    return format("PutStaticWithBarrier", value, sel);
	}
    }

    public static class New 
	extends SimpleJITInstruction 
	implements Definer, ConstantPoolReferer {
	SimpleJITOperand result;
	TypeName.Scalar tn;
	int cpindex;
	public New(SimpleJITOperand result, 
		   TypeName.Scalar tn, 
		   int cpindex) {
	    this.tn = tn;
	    this.result = result;
	    this.cpindex = cpindex;
	}
	public int getCPIndex() {
	    return cpindex;
	}
	public void setCPIndex(int cpi) {
	    cpindex = cpi;
	}
	public String toString() {
	    return format("New", result, tn);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
    }

    public static class NewArray 
	extends SimpleJITInstruction 
	implements Definer, User {
	SimpleJITOperand result;
	SimpleJITOperand dimension;
	TypeName.Array tn;
	public NewArray(SimpleJITOperand result, 
			SimpleJITOperand dimension,
			TypeName.Array tn) {
	    this.tn = tn;
	    this.result = result;
	    this.dimension = dimension;
	}
	public String toString() {
	    return format("NewArray", result, dimension, tn);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { dimension };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    dimension = uses[0];
	}
    }

    public static class ANewArray 
	extends SimpleJITInstruction 
	implements Definer, User, ConstantPoolReferer {
	SimpleJITOperand result;
	SimpleJITOperand dimension;
	TypeName.Array tn;
	int cpindex;
	public ANewArray(SimpleJITOperand result, 
			 SimpleJITOperand dimension,
			 TypeName.Array tn, 
			 int cpindex) {
	    this.tn = tn;
	    this.result = result;
	    this.dimension = dimension;
	    this.cpindex = cpindex;
	}
	public int getCPIndex() {
	    return cpindex;
	}
	public void setCPIndex(int cpi) {
	    cpindex = cpi;
	}
	public String toString() {
	    return format("ANewArray", result, dimension, tn);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { dimension };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    dimension = uses[0];
	}
    }

    public static class MultiANewArray 
	extends SimpleJITInstruction 
	implements Definer, User, ConstantPoolReferer {
	SimpleJITOperand result;
	SimpleJITOperand[] dimensions;
	TypeName.Array tn;
	int cpindex;
	public MultiANewArray(SimpleJITOperand result, 
			      SimpleJITOperand[] dimensions,
			      TypeName.Array tn, 
			      int cpindex) {
	    this.tn = tn;
	    this.result = result;
	    this.dimensions = dimensions;
	    this.cpindex = cpindex;
	}
	public int getCPIndex() {
	    return cpindex;
	}
	public void setCPIndex(int cpi) {
	    cpindex = cpi;
	}
	public String toString() {
	    return format("MultiANewArray", result, dimensions, tn);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return (SimpleJITOperand[])dimensions.clone();
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != dimensions.length)
		throw new Error();
	    dimensions = (SimpleJITOperand[])uses.clone();
	}
    }

    public static class CheckCast 
	extends SimpleJITInstruction 
	implements Definer, User, SideEffector, ConstantPoolReferer {
	TypeName.Compound tn;
	SimpleJITOperand result;
	SimpleJITOperand input;
	int cpindex;
	public CheckCast(SimpleJITOperand result,
			 SimpleJITOperand input,
			 TypeName.Compound tn,
			 int cpindex) {
	    this.tn = tn;
	    this.result = result;
	    this.input = input;
	    this.cpindex = cpindex;
	}
	public int getCPIndex() {
	    return cpindex;
	}
	public void setCPIndex(int cpi) {
	    cpindex = cpi;
	}
	public String toString() {
	    return format("CheckCast", result, input, tn);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { input };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    input = uses[0];
	}
    }

    public static class InstanceOf 
	extends SimpleJITInstruction 
	implements Definer, User, ConstantPoolReferer {
	TypeName.Compound tn;
	SimpleJITOperand result;
	SimpleJITOperand input;
	int cpindex;
	public InstanceOf(SimpleJITOperand result,
			  SimpleJITOperand input,
			  TypeName.Compound tn,
			  int cpindex) {
	    this.tn = tn;
	    this.result = result;
	    this.input = input;
	    this.cpindex = cpindex;
	}
	public int getCPIndex() {
	    return cpindex;
	}
	public void setCPIndex(int cpi) {
	    cpindex = cpi;
	}
	public String toString() {
	    return format("InstanceOf", result, input, tn);
	}
	public SimpleJITOperand[] getDefOperands() {
	    return new SimpleJITOperand[] { result };
	}
	public void setDefOperands(SimpleJITOperand[] r) {
	    if (r.length != 1)
		throw new Error();
	    result = r[0];
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { input };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    input = uses[0];
	}
    }

    public static class PollCheck 
	extends SimpleJITInstruction {
	public PollCheck() {
	}
	public String toString() {
	    return format("PollCheck");
	}
    }

    public static class Return 
	extends SimpleJITInstruction {
	public Return() {
	}
	public String toString() {
	    return format("Return");
	}
    }

    public static abstract class ReturnValue 
	extends SimpleJITInstruction 
	implements User {
	SimpleJITOperand returnValue;
	char type;
	protected ReturnValue(SimpleJITOperand returnValue,
			      char type) {
	    this.returnValue = returnValue;
	    this.type = type;
	}
	public char getType() {
	    return type;
	}
	public SimpleJITOperand getReturnValueOperand() {
	    return returnValue;
	}
	public abstract String toString();
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { returnValue };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    returnValue = uses[0];
	}
    }

    public static class IReturn extends ReturnValue {
	public IReturn(SimpleJITOperand returnValue) {
	    super(returnValue, TypeCodes.INT);
	}
	public String toString() {
	    return format("IReturn", returnValue);
	}
    }

    public static class LReturn extends ReturnValue {
	public LReturn(SimpleJITOperand returnValue) {
	    super(returnValue, TypeCodes.LONG);
	}
	public String toString() {
	    return format("LReturn", returnValue);
	}
    }

    public static class FReturn extends ReturnValue {
	public FReturn(SimpleJITOperand returnValue) {
	    super(returnValue, TypeCodes.FLOAT);
	}
	public String toString() {
	    return format("FReturn", returnValue);
	}
    }

    public static class DReturn extends ReturnValue {
	public DReturn(SimpleJITOperand returnValue) {
	    super(returnValue, TypeCodes.DOUBLE);
	}
	public String toString() {
	    return format("DReturn", returnValue);
	}
    }

    public static class AReturn extends ReturnValue {
	public AReturn(SimpleJITOperand returnValue) {
	    super(returnValue, TypeCodes.REFERENCE);
	}
	public String toString() {
	    return format("AReturn", returnValue);
	}
    }

    public static abstract class MonitorOp 
	extends SimpleJITInstruction 
	implements User {
	SimpleJITOperand obj;
	protected MonitorOp(SimpleJITOperand obj) {
	    this.obj = obj;
	}
	public abstract String toString();
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { obj };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    obj = uses[0];
	}
    }

    public static class MonitorEnter extends MonitorOp {
	protected MonitorEnter(SimpleJITOperand obj) {
	    super(obj);
	}
	public String toString() {
	    return format("MonitorEnter", obj);
	}
    }

    public static class MonitorExit extends MonitorOp {
	protected MonitorExit(SimpleJITOperand obj) {
	    super(obj);
	}
	public String toString() {
	    return format("MonitorExit", obj);
	}
    }

    public static class AThrow 
	extends SimpleJITInstruction 
	implements User {
	SimpleJITOperand obj;
	protected AThrow(SimpleJITOperand obj) {
	    this.obj = obj;
	}
	public SimpleJITOperand[] getUseOperands() {
	    return new SimpleJITOperand[] { obj };
	}
	public void setUseOperands(SimpleJITOperand[] uses) {
	    if (uses.length != 1)
		throw new Error();
	    obj = uses[0];
	}
	public String toString() {
	    return format("AThrow", obj);
	}
    }

    public interface Operators {
	int INT_ADD = 0;
	int INT_SUB = INT_ADD + 1;
	int INT_MUL = INT_SUB + 1;
	int INT_DIV = INT_MUL + 1;
	int INT_REM = INT_DIV + 1;
	int LONG_ADD = INT_REM + 1;
	int LONG_SUB = LONG_ADD + 1;
	int LONG_MUL = LONG_SUB + 1;
	int LONG_DIV = LONG_MUL + 1;
	int LONG_REM = LONG_DIV + 1;
	int FLOAT_ADD = LONG_REM + 1;
	int FLOAT_SUB = FLOAT_ADD + 1;
	int FLOAT_MUL = FLOAT_SUB + 1;
	int FLOAT_DIV = FLOAT_MUL + 1;
	int FLOAT_REM = FLOAT_DIV + 1;
	int DOUBLE_ADD = FLOAT_REM + 1;
	int DOUBLE_SUB = DOUBLE_ADD + 1;
	int DOUBLE_MUL = DOUBLE_SUB + 1;
	int DOUBLE_DIV = DOUBLE_MUL + 1;
	int DOUBLE_REM = DOUBLE_DIV + 1;
	int INT_SHL = DOUBLE_REM + 1;
	int INT_SHR = INT_SHL + 1;
	int INT_USHR = INT_SHR + 1;
	int LONG_SHL = INT_USHR + 1;
	int LONG_SHR = LONG_SHL + 1;
	int LONG_USHR = LONG_SHR + 1;
	int INT_NEG = LONG_USHR + 1;
	int INT_AND = INT_NEG + 1;
	int INT_OR = INT_AND + 1;
	int INT_XOR = INT_OR + 1;
	int LONG_NEG = INT_XOR + 1;
	int LONG_AND = LONG_NEG + 1;
	int LONG_OR = LONG_AND + 1;
	int LONG_XOR = LONG_OR + 1;
	int FLOAT_NEG = LONG_XOR + 1;
	int DOUBLE_NEG = FLOAT_NEG + 1;
	int INT2LONG = DOUBLE_NEG + 1;
	int INT2FLOAT = INT2LONG + 1;
	int INT2DOUBLE = INT2FLOAT + 1;
	int INT2CHAR = INT2DOUBLE + 1;
	int INT2SHORT = INT2CHAR + 1;
	int INT2BYTE = INT2SHORT + 1;
	int LONG2INT = INT2BYTE + 1;
	int LONG2FLOAT = LONG2INT + 1;
	int LONG2DOUBLE = LONG2FLOAT + 1;
	int FLOAT2INT = LONG2DOUBLE + 1;
	int FLOAT2LONG = FLOAT2INT + 1;
	int FLOAT2DOUBLE = FLOAT2LONG + 1;
	int DOUBLE2INT = FLOAT2DOUBLE + 1;
	int DOUBLE2LONG = DOUBLE2INT + 1;
	int DOUBLE2FLOAT = DOUBLE2LONG + 1;
	int LONG_CMP = DOUBLE2FLOAT + 1;
	int FLOAT_CMPG = LONG_CMP + 1;
	int FLOAT_CMPL = FLOAT_CMPG + 1;
	int DOUBLE_CMPG = FLOAT_CMPL + 1;
	int DOUBLE_CMPL = DOUBLE_CMPG + 1;
	int ARRAYLENGTH = DOUBLE_CMPL + 1;
	int INVOKEVIRTUAL = ARRAYLENGTH + 1;
	int INVOKESPECIAL = INVOKEVIRTUAL + 1;
	int INVOKESTATIC = INVOKESPECIAL + 1;
	int INVOKEINTERFACE = INVOKESTATIC + 1;
	int INVOKE_SYSTEM = INVOKEINTERFACE + 1;
	int INVOKE_NATIVE = INVOKE_SYSTEM + 1;
	int GETFIELD = INVOKE_NATIVE + 1;
	int PUTFIELD = GETFIELD + 1;
	int PUTFIELD_WITH_BARRIER = PUTFIELD + 1;
    }

    public static String[] operatorNames = 
	new String[] {
	    "IntAdd",
	    "IntSub",
	    "IntMul",
	    "IntDiv",
	    "IntRem",
	    "LongAdd",
	    "LongSub",
	    "LongMul",
	    "LongDiv",
	    "LongRem",
	    "FloatAdd",
	    "FloatSub",
	    "FloatMul",
	    "FloatDiv",
	    "FloatRem",
	    "DoubleAdd",
	    "DoubleSub",
	    "DoubleMul",
	    "DoubleDiv",
	    "DoubleRem",
	    "IntShl",
	    "IntShr",
	    "IntUshr",
	    "LongShl",
	    "LongShr",
	    "LongUshr",
	    "IntNeg",
	    "IntAnd",
	    "IntOr",
	    "IntXor",
	    "LongNeg",
	    "LongAnd",
	    "LongOr",
	    "LongXor",
	    "FloatNeg",
	    "DoubleNeg",
	    "Int2Long",
	    "Int2Float",
	    "Int2Double",
	    "Int2Char",
	    "Int2Short",
	    "Int2Byte",
	    "Long2Int",
	    "Long2Float",
	    "Long2Double",
	    "Float2Int",
	    "Float2Long",
	    "Float2Double",
	    "Double2Int",
	    "Double2Long",
	    "Double2Float",
	    "LongCmp",
	    "FLoatCmpg",
	    "FloatCmpl",
	    "DoubleCmpg",
	    "DoubleCmgl",
	    "ArrayLength",
	    "Invokevirtual",
	    "Invokespecial",
	    "Invokestatic",
	    "Invokeinterface",
	    "InvokeSystem",
	    "InvokeNative",
	    "GetField",
	    "PutField",
	    "ReadBarrier"
	};


}
