package s3.services.simplejit.ir;

import ovm.util.Arrays;
import ovm.util.ArrayList;
import ovm.util.HTint2Object;
import ovm.util.Iterator;
import ovm.util.IdentityHashMap;
import ovm.core.repository.TypeName;
import s3.services.simplejit.ir.SimpleJITOperand.*;
import s3.services.simplejit.ir.SimpleJITInstruction.*;
import s3.services.simplejit.util.SimpleJITIdentitySet;

public class SimpleJITBasicBlock {

    public static class Set {
	private SimpleJITBasicBlock beginBlock;
	private SimpleJITBasicBlock endBlock;
	private List blockList; // beginBlock and endBlock are also in
				// the list
	public Set(HTint2Object entryPC2Block) {
	    beginBlock = (SimpleJITBasicBlock)entryPC2Block.get(0);
	    blockList = new List();
	    int[] keys = entryPC2Block.keys();
	    Arrays.sort(keys);
	    for(int i = 0; i < keys.length; i++)
		blockList.add((SimpleJITBasicBlock)entryPC2Block
			      .get(keys[i]));
	    makeEndBlock();
	}
	/**
	 * Make a unique end block for backward data flow
	 * analyses.
	 */
	private void makeEndBlock() {
	    if (blockList.size() <= 1) {
		endBlock = beginBlock;
		return;
	    }
	    int numOfFlowEnds = 0;
	    SimpleJITBasicBlock oneOfFlowEnd = null;
	    for(Iterator it = blockList.iterator();
		it.hasNext(); ) {
		SimpleJITBasicBlock b =
		    (SimpleJITBasicBlock)it.next();
		if (b.isFlowEndBlock()) {
		    numOfFlowEnds++;
		    oneOfFlowEnd = b;
		}
	    }
	    if (numOfFlowEnds <= 1) {
		endBlock = oneOfFlowEnd;
		return;
	    }
	    SimpleJITBasicBlock dummyEndBlock =
		new SimpleJITBasicBlock();
	    for(Iterator it = blockList.iterator();
		it.hasNext(); ) {
		SimpleJITBasicBlock b =
		    (SimpleJITBasicBlock)it.next();
		if (b.isFlowEndBlock()) {
		    b.addEdgeTo(dummyEndBlock);
		}
	    }
	    dummyEndBlock.append(new SimpleJITInstruction.Dummy());
	    blockList.add(dummyEndBlock);
	    this.endBlock = dummyEndBlock;
	}
	public SimpleJITBasicBlock getEndBlock() {
	    return endBlock;
	}
	public SimpleJITBasicBlock getBeginBlock() {
	    return beginBlock;
	}
	public SimpleJITBasicBlock.List getBlockList() {
	    return blockList;
	}
	public String toString() {
	    String message = "";
	    for(Iterator it = blockList.iterator();
		it.hasNext(); ) {
		SimpleJITBasicBlock b =
		    (SimpleJITBasicBlock)it.next();
		message += b.toString() + " : \n";
		message += b.getLoopsAsString() + "\n";
		message += b.getInEdgesAsString() + "\n";
		message += b.getOutEdgesAsString() + "\n";
//		message += b.getInstructionsAsStringWithReachingInAndOutDefs() 
//		    + "\n";
//		message += b.getInstructionsAsStringWithLiveInAndOutUses() 
//		    + "\n";
		message += b.getInstructionsAsString() + "\n";
	    }
	    return message;
	}
    }

    public static class List {
	private ArrayList internal;
	public List() {
	    internal = new ArrayList();
	}
	public Iterator iterator() {
	    return internal.iterator();
	}
	public void add(SimpleJITBasicBlock b) {
	    internal.add(b);
	}
	public boolean contains(SimpleJITBasicBlock b) {
	    return internal.contains(b);
	}
	public void addUnlessContained(SimpleJITBasicBlock b) {
	    if (! contains(b))
		add(b);
	}
	public void addAll(SimpleJITBasicBlock[] b) {
	    for(int i = 0; i < b.length; i++)
		internal.add(b[i]);
	}
	public void addAll(SimpleJITBasicBlock.List blockList) {
	    for(Iterator it = blockList.iterator();
		it.hasNext(); )
		add((SimpleJITBasicBlock)it.next());
	}
	public int size() {
	    return internal.size();
	}
	public boolean empty() {
	    return internal.size() == 0;
	}
	public void remove(SimpleJITBasicBlock b) {
	    int idx = internal.indexOf(b);
	    if (idx >= 0)
		internal.remove(idx);
	}
	public SimpleJITBasicBlock remove() {
	    if (! empty())
		// FIFO
		return (SimpleJITBasicBlock)internal.remove(0);
	    else
		return null;
	}
	public List copy() {
	    List l = new List();
	    l.internal = (ArrayList)this.internal.clone();
	    return l;
	}
	public SimpleJITBasicBlock[] toArray() {
	    int size = internal.size();
	    SimpleJITBasicBlock[] ret =
		new SimpleJITBasicBlock[size];
	    for(int i = 0; i < size; i++)
		ret[i] = 
		    (SimpleJITBasicBlock)internal.get(i);
	    return ret;
	}
    }

    public abstract static class EdgeType {
    }
    private static class BackEdge extends EdgeType {
	public String toString() { return "-B"; }
    }
    private static class TreeEdge extends EdgeType {
	public String toString() { return "-T"; }
    }
    private static class ForwardEdge extends EdgeType {
	public String toString() { return "-F"; }
    }
    private static class CrossEdge extends EdgeType {
	public String toString() { return "-C"; }
    }
    // Singletons
    public static final EdgeType BACK_EDGE = new BackEdge();
    public static final EdgeType TREE_EDGE = new TreeEdge();
    public static final EdgeType FORWARD_EDGE = new ForwardEdge();
    public static final EdgeType CROSS_EDGE = new CrossEdge();

    private static int blockIDCounter = 0;

    private int ID;
    /** The bytecode PC at which this block starts */
    private int entryPC; 
    /** The bytecode PC at which this block ends (== the entryPC of
     * the next block). */
    private int exitPC;
    private ArrayList instructions;
    private ArrayList outEdges;
    private ArrayList inEdges;
    private SimpleJITOperand.Stack op_stack;
    private boolean isHandlerBlock;
    private boolean isFlowEndBlock;
    private TypeName.Scalar exceptionToHandle; // valid if isHandlerBlock == true
    private SimpleJITIdentitySet reaching_in_defs;
    private SimpleJITIdentitySet reaching_out_defs;
    private SimpleJITIdentitySet live_in_uses;
    private SimpleJITIdentitySet live_out_uses;
    private int preorder = 0;
    private int rpostorder = 0;
    private IdentityHashMap outEdgeTypeMap;

    /** The entry blocks of the loops which contain this block */
    private SimpleJITIdentitySet loops; 
    /** Whether or not this block is a loop entry block */
    private boolean loopEntry;
    /** The set of blocks that belong to the loop whose entry is this
     * block */
    private SimpleJITIdentitySet blocksInTheLoopStartingAtThisBlock;

    /**
     * True if there is no bytecode instruction processed since an
     * instruction is last appended.
     */
    private boolean contiguous;

    public SimpleJITBasicBlock(int entryPC) {
	ID = blockIDCounter++;
	this.entryPC = entryPC;
	this.exitPC = -1;
	instructions = new ArrayList();
	outEdges = new ArrayList();
	inEdges = new ArrayList();
	isHandlerBlock = false;
	isFlowEndBlock = false;
	reaching_in_defs = new SimpleJITIdentitySet();
	reaching_out_defs = new SimpleJITIdentitySet();
	live_in_uses = new SimpleJITIdentitySet();
	live_out_uses = new SimpleJITIdentitySet();
	outEdgeTypeMap = new IdentityHashMap();
	loops = new SimpleJITIdentitySet();
	loopEntry = false;
	blocksInTheLoopStartingAtThisBlock = new SimpleJITIdentitySet();
    }

    public SimpleJITBasicBlock() {
	ID = blockIDCounter++;
	entryPC = -1;
	exitPC = -1;
	instructions = new ArrayList();
	outEdges = new ArrayList();
	inEdges = new ArrayList();
	isHandlerBlock = false;
	isFlowEndBlock = false;
	reaching_in_defs = new SimpleJITIdentitySet();
	reaching_out_defs = new SimpleJITIdentitySet();
	live_in_uses = new SimpleJITIdentitySet();
	live_out_uses = new SimpleJITIdentitySet();
	outEdgeTypeMap = new IdentityHashMap();
	loops = new SimpleJITIdentitySet();
	loopEntry = false;
	blocksInTheLoopStartingAtThisBlock = new SimpleJITIdentitySet();
    }

    public void setBlockSetStartingAtThisBlock(SimpleJITIdentitySet set) {
	blocksInTheLoopStartingAtThisBlock = set.copy();
    }

    public int getLoopDepth() {
	return loops.size();
    }

    public void setLoopEntry() {
	loopEntry = true;
    }

    public boolean isLoopEntry() {
	return loopEntry;
    }

    public void addLoop(SimpleJITBasicBlock entryBlock) {
	loops.add(entryBlock);
    }

    public void setEdgeType(SimpleJITBasicBlock s, EdgeType e) {
	if (! outEdges.contains(s))
	    throw new Error();
	outEdgeTypeMap.put(s, e);
    }
    public EdgeType getEdgeType(SimpleJITBasicBlock s) {
	if (! outEdges.contains(s))
	    throw new Error();
	return (EdgeType)outEdgeTypeMap.get(s);
    }

    public int getPreorder() {
	return preorder;
    }
    public void setPreorder(int po) {
	preorder = po;
    }
    public int getReversePostOrder() {
	return rpostorder;
    }
    public void setReversePostOrder(int rpo) {
	rpostorder = rpo;
    }

    /**
     * Replace this block with the given block set in the CFG.
     */
    public void replaceWith(SimpleJITBasicBlock.Set blockSet) {
	SimpleJITBasicBlock beginBlock = blockSet.getBeginBlock();
	SimpleJITBasicBlock endBlock = blockSet.getEndBlock();

	// Adjust edges

	// in-edges
	ArrayList iedges = this.inEdges;
	int iesize = iedges.size();
	for(int i = 0; i < iesize; i++) {
	    SimpleJITBasicBlock pred =
		(SimpleJITBasicBlock)iedges.get(i);
	    pred.removeFromOutEdges(this);
    //	    this.removeFromInEdges(pred);
	    pred.addEdgeTo(beginBlock);
	    
	    // Adjust the branches in the branching instruction as
	    // well
	    pred.replaceBranchOperand(this, beginBlock);
	}

	// out-edges
	ArrayList oedges = this.outEdges;
	int oesize = oedges.size();
	for(int i = 0; i < oesize; i++) {
	    SimpleJITBasicBlock succ = 
		(SimpleJITBasicBlock)oedges.get(i);
	    succ.removeFromInEdges(this);
    //	    this.removeFromOutEdges(succ);
	    endBlock.addEdgeTo(succ);
	}

	// loops
	if (this.isLoopEntry()) {
	    beginBlock.setLoopEntry();
	    beginBlock.addLoop(beginBlock);

	    // update loop head
	    for(Iterator it = 
		    blocksInTheLoopStartingAtThisBlock.iterator();
		it.hasNext(); ) {
		SimpleJITBasicBlock b =
		    (SimpleJITBasicBlock)it.next();
		if (b.loops.contains(this)) {
		    b.loops.remove(this);
		    b.loops.add(beginBlock);
		} else
		    throw new Error();
	    }
	}
	// update the loops of the inlinee blocks
	SimpleJITBasicBlock.List blockList = blockSet.getBlockList();
	for(Iterator it = blockList.iterator();
	    it.hasNext(); ) {
	    SimpleJITBasicBlock b = (SimpleJITBasicBlock)it.next();
	    for(Iterator it1 = this.loops.iterator();
		it1.hasNext();) {
		b.addLoop((SimpleJITBasicBlock)it1.next());
	    }
	}
    }

    /**
     * Replace a branch operand (a target block) in the last
     * instruction of this block.
     */
    public void replaceBranchOperand(SimpleJITBasicBlock from,
				     SimpleJITBasicBlock to) {
	SimpleJITInstruction lastInst =
	    this.getLastInstruction();
	if (lastInst != null 
	    && lastInst.isBrancher()) {
	    Brancher brancher = lastInst.asBrancher();
	    SimpleJITBranchOperand[] branches =
		brancher.getBranchOperands();
	    for(int i = 0; i < branches.length; i++) {
		SimpleJITBranchOperand bop = branches[i];
		if (from.equals(bop.getBlock()))
		    bop.setBlock(to);
	    }
	    brancher.setBranchOperands(branches);
	}
    }

    /**
     * Split this block at the given instruction (which must belong to
     * this block) into three blocks, 1) the block that contains the
     * instructions that come before the instruction, 2) the block
     * that contains only the instruction, and 3) the block that
     * contains the instructions that come after the
     * instruction. Edges are established from the predecessors of the
     * original block to block 1, from block 1 to block 2, block 2 to
     * block 3, and block 3 to the successors of the originl block. If
     * the instruction is the first instruction in the original block,
     * then block 1 and block 2 are the same block, and if the
     * instruction is the last instruction in the original block,
     * block 2 and block 3 are the same block. If the instruction is
     * the only instruction in the original block, this method has no
     * effect. The instance of this block is reused as block 1.
     * @param inst the instruction
     * @param block123 the SimpleJITBasicBlock array of length 3 which
     * will contain block 1, 2 and 3, as the return value.
     * @return true if the block was split, false otherwise.
     */
    public boolean splitAtInstruction(SimpleJITInstruction inst,
				      SimpleJITBasicBlock[] block123) {
	if (block123 == null
	    || block123.length != 3)
	    throw new Error();
	int isize0 = instructions.size();
	boolean doesBelong = false;
	boolean isFirst = false;
	boolean isLast = false;
	int instIndex = -1;
	for(int i = 0; i < isize0; i++) {
	    if (inst == instructions.get(i)) {
		doesBelong = true;
		instIndex = i;
		if (i == 0)
		    isFirst = true;
		if (i == isize0 - 1)
		    isLast = true;
		break;
	    }
	}
	if (! doesBelong)
	    throw new Error("the instruction doesn't belong to the block.");
	if (isFirst && isLast) {
	    block123[0] = this;
	    block123[1] = this;
	    block123[2] = this;
	    return false; // no-op
	} else if (isFirst) {
	    SimpleJITBasicBlock b3 = new SimpleJITBasicBlock();
	    
	    // Move instructions
	    int isize = instructions.size();
	    for(int i = instIndex + 1; i < isize; i++) {
		b3.append((SimpleJITInstruction)instructions.get(i));
	    }
	    for(int i = instIndex + 1; i < isize; i++) {
		instructions.remove(instIndex + 1);
	    }

	    // Adjust edges

	    // Remove the original block from the in-edges of its
	    // successors
	    ArrayList oedges = this.outEdges;
	    int oesize = oedges.size();
	    for(int i = 0; i < oesize; i++) {
		SimpleJITBasicBlock succ = 
		    (SimpleJITBasicBlock)oedges.get(i);
		succ.removeFromInEdges(this);
	    }

	    // Establish edges
	    ArrayList originalOutEdges = this.clearOutEdges();
	    this.addEdgeTo(b3);
	    int ooesize = originalOutEdges.size();
	    for(int i = 0; i < ooesize; i++) {
		SimpleJITBasicBlock succ = 
		    (SimpleJITBasicBlock)originalOutEdges.get(i);
		b3.addEdgeTo(succ);
	    }

	    b3.isFlowEndBlock = this.isFlowEndBlock;
	    this.isFlowEndBlock = false;
	    b3.loops = this.loops.copy();

	    block123[0] = this;
	    block123[1] = this;
	    block123[2] = b3;
	    return true;
	} else if (isLast) {
	    SimpleJITBasicBlock b3 = new SimpleJITBasicBlock();
	    
	    // Move instructions
	    b3.instructions.add(inst);
	    instructions.remove(instIndex);

	    // Adjust edges

	    // Remove the original block from the in-edges of its
	    // successors
	    ArrayList oedges = this.outEdges;
	    int oesize = oedges.size();
	    for(int i = 0; i < oesize; i++) {
		SimpleJITBasicBlock succ = 
		    (SimpleJITBasicBlock)oedges.get(i);
		succ.removeFromInEdges(this);
	    }

	    // Establish edges
	    ArrayList originalOutEdges = this.clearOutEdges();
	    this.addEdgeTo(b3);
	    int ooesize = originalOutEdges.size();
	    for(int i = 0; i < ooesize; i++) {
		SimpleJITBasicBlock succ = 
		    (SimpleJITBasicBlock)originalOutEdges.get(i);
		b3.addEdgeTo(succ);
	    }

	    b3.isFlowEndBlock = this.isFlowEndBlock;
	    this.isFlowEndBlock = false;
	    b3.loops = this.loops.copy();

	    block123[0] = this;
	    block123[1] = b3;
	    block123[2] = b3;
	    return true;
	} else {
	    SimpleJITBasicBlock b2 = new SimpleJITBasicBlock();
	    SimpleJITBasicBlock b3 = new SimpleJITBasicBlock();
	    
	    // Move instructions
	    b2.instructions.add(inst);
	    int isize = instructions.size();
	    for(int i = instIndex + 1; i < isize; i++) {
		b3.instructions.add(instructions.get(i));
	    }
	    for(int i = instIndex + 1; i < isize; i++) {
		instructions.remove(instIndex + 1);
	    }
	    instructions.remove(instIndex);

	    // Adjust edges

	    // Remove the original block from the in-edges of its
	    // successors
	    ArrayList oedges = this.outEdges;
	    int oesize = oedges.size();
	    for(int i = 0; i < oesize; i++) {
		SimpleJITBasicBlock succ = 
		    (SimpleJITBasicBlock)oedges.get(i);
		succ.removeFromInEdges(this);
	    }

	    // Establish edges
	    ArrayList originalOutEdges = this.clearOutEdges();
	    this.addEdgeTo(b2);
	    b2.addEdgeTo(b3);
	    int ooesize = originalOutEdges.size();
	    for(int i = 0; i < ooesize; i++) {
		SimpleJITBasicBlock succ = 
		    (SimpleJITBasicBlock)originalOutEdges.get(i);
		b3.addEdgeTo(succ);
	    }

	    b3.isFlowEndBlock = this.isFlowEndBlock;
	    this.isFlowEndBlock = false;
	    b2.loops = this.loops.copy();
	    b3.loops = this.loops.copy();

	    block123[0] = this;
	    block123[1] = b2;
	    block123[2] = b3;
	    return true;
	}
    }


    /**
     * Split this block into this block (the first part of the old
     * block) and the given block (the second part of the old block)
     * in terms of edges. This does not deal with instructions, flow
     * information, etc. This is used when first constructing the
     * control flow graph.
     */
    public void split(SimpleJITBasicBlock new_successor) {
	new_successor.inEdges = new ArrayList();
	new_successor.inEdges.add(this);

	// Update the in-edges pointing to this.
	ArrayList oedges = this.outEdges;
	int oesize = oedges.size();
	for(int i = 0; i < oesize; i++) {
	    SimpleJITBasicBlock succ = 
		(SimpleJITBasicBlock)oedges.get(i);
	    ArrayList iedges = succ.inEdges;
	    int iesize = iedges.size();
	    for(int j = 0; j < iesize; j++) {
		SimpleJITBasicBlock pred =
		    (SimpleJITBasicBlock)iedges.get(j);
		if (pred == this)
		    iedges.set(j, new_successor);
	    }
	}

	new_successor.outEdges = oedges;
	new_successor.isFlowEndBlock = this.isFlowEndBlock;
	this.isFlowEndBlock = false;
	this.outEdges = new ArrayList();
	this.outEdges.add(new_successor);
    }

    public void markAsHandlerBlock(TypeName.Scalar ex) {
	isHandlerBlock = true;
	exceptionToHandle = ex;
    }

    public boolean isHandlerBlock() {
	return isHandlerBlock;
    }

    public void setFlowEndBlock(boolean isFlowEndBlock) {
	this.isFlowEndBlock = isFlowEndBlock;
    }

    public void markAsFlowEndBlock() {
	isFlowEndBlock = true;
    }

    public boolean isFlowEndBlock() {
	return isFlowEndBlock;
    }

    public int getEntryPC() {
	if (entryPC < 0)
	    throw new Error();
	return entryPC;
    }

    public int getExitPC() {
	if (exitPC < 0)
	    throw new Error();
	return exitPC;
    }
    public void setExitPC(int epc) {
	exitPC = epc;
    }
    public void setOpStack(SimpleJITOperand.Stack s) {
	if (op_stack != null) {
	    if (op_stack.height() != s.height())
		throw new Error("Tried to set an incompatible opstack"
				+ "\n op_stack : " + op_stack
				+ "\n s : " + s);
	}
	op_stack = s;
    }
    public SimpleJITOperand.Stack getOpStack() {
	return op_stack;
    }
    public void append(SimpleJITInstruction i) {
	contiguous = true;
	instructions.add(i);
    }
    public void appendNothing() {
	contiguous = false;
    }
    public boolean isContiguous() {
	return contiguous;
    }

    public SimpleJITInstruction getFirstInstruction() {
	int size = instructions.size();
	if (size > 0)
	    return (SimpleJITInstruction)
		instructions.get(0);
	else
	    return null;
    }
    public SimpleJITInstruction getLastInstruction() {
	int size = instructions.size();
	if (size > 0)
	    return (SimpleJITInstruction)
		instructions.get(size - 1);
	else
	    return null;
    }

    public void addEdgeFrom(SimpleJITBasicBlock b) {
	if (! this.inEdges.contains(b))
	    this.inEdges.add(b);
	if (! b.outEdges.contains(this))
	    b.outEdges.add(this);
    }

    public void addEdgeFrom(SimpleJITBasicBlock[] blocks) {
	for(int i = 0; i < blocks.length; i++) {
	    addEdgeFrom(blocks[i]);
	}
    }

    public void addEdgeTo(SimpleJITBasicBlock b) {
	if (! this.outEdges.contains(b))
	    this.outEdges.add(b);
	if (! b.inEdges.contains(this))
	    b.inEdges.add(this);
    }

    public void addEdgeTo(SimpleJITBasicBlock[] blocks) {
	for(int i = 0; i < blocks.length; i++) {
	    addEdgeTo(blocks[i]);
	}
    }

    public void removeFromInEdges(SimpleJITBasicBlock b) {
	int index = inEdges.indexOf(b);
	if (index >= 0)
	    inEdges.remove(index);
    }

    public void removeFromOutEdges(SimpleJITBasicBlock b) {
	int index = outEdges.indexOf(b);
	if (index >= 0)
	    outEdges.remove(index);
    }

    /**
     * @return the copy of the old out edge list
     */
    public ArrayList clearOutEdges() {
	ArrayList oldOutEdges = (ArrayList)outEdges.clone();
	outEdges.clear();
	return oldOutEdges;
    }

    public SimpleJITBasicBlock[] getPredecessors() {
	int size = inEdges.size();
	SimpleJITBasicBlock[] s = new SimpleJITBasicBlock[size];
	for(int i = 0; i < s.length; i++) {
	    s[i] = (SimpleJITBasicBlock)inEdges.get(i);
	}
	return s;
    }

    public SimpleJITBasicBlock[] getSuccessors() {
	int size = outEdges.size();
	SimpleJITBasicBlock[] s = new SimpleJITBasicBlock[size];
	for(int i = 0; i < s.length; i++) {
	    s[i] = (SimpleJITBasicBlock)outEdges.get(i);
	}
	return s;
    }

    public String toString() {
	String s = "BB" + ID;
	if (isHandlerBlock)
	    s += "H";
	if (isFlowEndBlock)
	    s += "E";
	if (entryPC >= 0)
	    s += "@PC" + entryPC; // + "-" + exitPC;
	else
	    s += "@NOPC";
	return s;
    }

    public String getLoopsAsString() {
	String s = "Loop : ";
	s += "depth = " + getLoopDepth() + " : ";
	if (loopEntry)
	    s += "ENTRY : ";
	for(Iterator it = loops.iterator();
	    it.hasNext(); ) {
	    SimpleJITBasicBlock b =
		(SimpleJITBasicBlock)it.next();
	    s += b.toString() + " ";
	}
	return s;
    }

    public String getOutEdgesAsString() {
	String s = "* --> ";
	for(int i = 0; i < outEdges.size(); i++) {
	    SimpleJITBasicBlock succ = 
		(SimpleJITBasicBlock)outEdges.get(i);
	    EdgeType type = getEdgeType(succ);
	    if (type != null)
		s += succ.toString() + "" + type.toString() + " ";
	    else
		s += succ.toString() + " ";
	}
	return s;
    }

    public String getInEdgesAsString() {
	String s = " --> *";
	for(int i = 0; i < inEdges.size(); i++) {
	    s = inEdges.get(i).toString() + " " + s;
	}
	return s;
    }

    public String getInstructionsAsString() {
	String s = "\n";
	for(int i = 0; i < instructions.size(); i++) {
	    s += "\t" + instructions.get(i) + "\n";
	}
	return s;
    }

    private String getSimpleSetString(SimpleJITIdentitySet set) {
	String r = "[ ";
	for(Iterator it = set.iterator();
	    it.hasNext(); ) {
	    Object o = it.next();
	    if (o instanceof SimpleJITTemporaryDefOperand)
		r += ((SimpleJITTemporaryDefOperand)o).toSimpleString();
	    else if (o instanceof SimpleJITTemporaryUseOperand)
		r += ((SimpleJITTemporaryUseOperand)o).toSimpleString();
	    else if (o instanceof SimpleJITVirtualRegisterOperand)
		r += ((SimpleJITVirtualRegisterOperand)o).toSimpleString();
	    else
		throw new Error("Found unexpected operand type " + o);
	    r += " ";
	}
	r += "]";
	return r;
    }

    public String getInstructionsAsStringWithReachingInAndOutDefs() {
	String s = "\n";
	for(int i = 0; i < instructions.size(); i++) {
	    SimpleJITInstruction inst = 
		(SimpleJITInstruction)instructions.get(i);
	    s += "\t\t" + getSimpleSetString(inst.getReachingInDefs()) + "\n";
	    s += "\t" + inst.toString() + "\n";
	    s += "\t\t" + getSimpleSetString(inst.getReachingOutDefs()) + "\n";
	}
	return s;
    }

    public String getInstructionsAsStringWithLiveInAndOutUses() {
	String s = "\n";
	for(int i = 0; i < instructions.size(); i++) {
	    SimpleJITInstruction inst = 
		(SimpleJITInstruction)instructions.get(i);
	    s += "\t\t" + getSimpleSetString(inst.getLiveInUses()) + "\n";
	    s += "\t" + inst.toString() + "\n";
	    s += "\t\t" + getSimpleSetString(inst.getLiveOutUses()) + "\n";
	}
	return s;
    }

    public SimpleJITInstruction[] getInstructions() {
	SimpleJITInstruction[] ret =
	    new SimpleJITInstruction[instructions.size()];
	instructions.toArray(ret);
	return ret;
    }

    public void removeInstruction(SimpleJITInstruction inst) {
	int index = instructions.indexOf(inst);
	if (index >= 0)
	    instructions.remove(index);
    }

    public void replaceInstruction(SimpleJITInstruction from,
				   SimpleJITInstruction to) {
	int index = instructions.indexOf(from);
	if (index >= 0)
	    instructions.set(index, to);
    }

    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 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 _setReachingInDefs(SimpleJITIdentitySet s) {
	reaching_in_defs = s;
    }
    public void _setReachingOutDefs(SimpleJITIdentitySet s) {
	reaching_out_defs = s;
    }
    public SimpleJITIdentitySet _getReachingInDefs() {
	return reaching_in_defs;
    }
    public SimpleJITIdentitySet _getReachingOutDefs() {
	return reaching_out_defs;
    }

    public void _setLiveInUses(SimpleJITIdentitySet s) {
	live_in_uses = s;
    }
    public void _setLiveOutUses(SimpleJITIdentitySet s) {
	live_out_uses = s;
    }
    public SimpleJITIdentitySet _getLiveInUses() {
	return live_in_uses;
    }
    public SimpleJITIdentitySet _getLiveOutUses() {
	return live_out_uses;
    }

}
