package s3.services.simplejit.optimization;

import ovm.core.OVMBase;
import ovm.core.repository.TypeCodes;
import ovm.util.IdentityHashMap;
import ovm.util.Iterator;
import ovm.util.HashMap;
import s3.services.simplejit.SimpleJITCompileFilter;
import s3.services.simplejit.SimpleJITAllCompileFilter;
import s3.services.simplejit.ir.SimpleJITOperand.*;
import s3.services.simplejit.ir.*;
import s3.services.simplejit.CompilerVMInterface;
import s3.services.simplejit.util.SimpleJITIdentitySet;
import s3.services.simplejit.util.SimpleJITStack;
import s3.services.simplejit.SimpleJITLogger;
import s3.core.domain.S3Method;
import java.io.*;
import java.util.Date;

public class SimpleJITRegisterAllocator {

    public static class ResultWriter {
	private PrintStream result_stream;
	public ResultWriter(String filename) {
	    try {
		result_stream = 
		    new PrintStream
		    (new BufferedOutputStream
		     (new FileOutputStream(filename)),
		     true);
	    } catch(IOException e) {
		e.printStackTrace();
	    }
	}
	public void println(String s) {
	    result_stream.println(s);
	}
	public void print(String s) {
	    result_stream.print(s);
	}
    }

    public static class Driver {

	private final SimpleJITDataFlowAnalyzer.Driver dfa_driver;
	private final SimpleJITCompileFilter compileFilter;
	private final SimpleJITResultWriteFilter resultWriteFilter;
	private final int allocator;
	private final ResultWriter resultWriter;
	private static final ResultWriter codeSizeWriter 
	    = new ResultWriter("codesize.result");

	public Driver(SimpleJITDataFlowAnalyzer.Driver dfa_driver, 
		      int allocator) {
	    this.dfa_driver = dfa_driver;
	    this.allocator = allocator;
	    this.compileFilter = new SimpleJITAllCompileFilter();
	    this.resultWriteFilter = new SimpleJITNoResultWriteFilter();
	    this.resultWriter = null;
	}

	public Driver(SimpleJITDataFlowAnalyzer.Driver dfa_driver,
		      int allocator,
		      SimpleJITCompileFilter compileFilter) {
	    this.dfa_driver = dfa_driver;
	    this.allocator = allocator;
	    this.compileFilter = compileFilter;
	    this.resultWriteFilter = new SimpleJITNoResultWriteFilter();
	    this.resultWriter = null;
	}

	public Driver(SimpleJITDataFlowAnalyzer.Driver dfa_driver,
		      int allocator,
		      SimpleJITCompileFilter compileFilter,
		      SimpleJITResultWriteFilter resultWriteFilter,
		      ResultWriter resultWriter) {
	    this.dfa_driver = dfa_driver;
	    this.allocator = allocator;
	    this.compileFilter = compileFilter;
	    this.resultWriteFilter = resultWriteFilter;
	    this.resultWriter = resultWriter;
	}

	public Driver(SimpleJITDataFlowAnalyzer.Driver dfa_driver,
		      int allocator,
		      SimpleJITResultWriteFilter resultWriteFilter,
		      ResultWriter resultWriter) {
	    this.dfa_driver = dfa_driver;
	    this.allocator = allocator;
	    this.compileFilter = new SimpleJITAllCompileFilter();
	    this.resultWriteFilter = resultWriteFilter;
	    this.resultWriter = resultWriter;
	}

	public SimpleJITIRCode run
	    (S3Method method,
	     CompilerVMInterface compilerVMInterface) {

	    if (! compileFilter.shouldCompile(method))
		return null;

	    SimpleJITIRCode sjMethod =
		dfa_driver.run(method,
			       compilerVMInterface);
	    if (sjMethod == null)
		return null;

	    runRegisterAllocator(sjMethod,
				 compilerVMInterface);

	    return sjMethod;
	}	    
	private void runRegisterAllocator
	    (SimpleJITIRCode sjMethod,
	     CompilerVMInterface compilerVMInterface) {
	    SimpleJITRegisterAllocator regAllocator = 
		new SimpleJITRegisterAllocator(sjMethod, 
					       allocator, 
					       resultWriteFilter, 
					       resultWriter,
					       codeSizeWriter);
	    regAllocator.run();
	    regAllocator.print();
	}
    }

    /** The list of the register allocation algorithms available */
    public final static int ALLOCATOR_LINEAR_PACKING                    = 1;
    public final static int ALLOCATOR_GREEDY_PACKING                    = 2;
    public final static int ALLOCATOR_EXACT_GRAPH_COLORING              = 3;
    public final static int ALLOCATOR_INCREMENTAL_GRAPH_COLORING        = 4;
    public final static int ALLOCATOR_GREEDY_EXACT_GRAPH_COLORING       = 5;
    public final static int ALLOCATOR_GREEDY_INCREMENTAL_GRAPH_COLORING = 6;

    public final static int ALLOCATOR_NORMAL_GRAPH_COLORING             = 7;

    /** 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 = true;

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

    private static final boolean WRITE_CODESIZE = true;

    /** The set of all webs. Computed once by computeWebs() */
    private SimpleJITIdentitySet webSet;

    private long allocationTime = 0L;
    private Date startTime;
    private Date endTime;

    /** The set (SimpleJITIdentitySet) of all web sets
     * (SimpleJITWebTemporaryOperand.Set) which are allocated to the
     * same virtual register. Changes on each register allocation. */
    private SimpleJITIdentitySet mergedWebSetSet_I = new SimpleJITIdentitySet();
    private SimpleJITIdentitySet mergedWebSetSet_F = new SimpleJITIdentitySet();
    private SimpleJITIdentitySet mergedWebSetSet_A = new SimpleJITIdentitySet();
    private SimpleJITIdentitySet mergedWebSetSet_D = new SimpleJITIdentitySet();
    private SimpleJITIdentitySet mergedWebSetSet_J = new SimpleJITIdentitySet();
    private SimpleJITIdentitySet mergedWebSetSet_U = new SimpleJITIdentitySet();

    /** The virtual register table */
    private SimpleJITVirtualRegisterOperand[] vregTable_I;
    private SimpleJITVirtualRegisterOperand[] vregTable_A;
    private SimpleJITVirtualRegisterOperand[] vregTable_J;
    private SimpleJITVirtualRegisterOperand[] vregTable_F;
    private SimpleJITVirtualRegisterOperand[] vregTable_D;
    private SimpleJITVirtualRegisterOperand[] vregTable_U;

    private VirtualRegisterFactory vrf = new VirtualRegisterFactory();

    private SimpleJITBasicBlock.Set blockSet;

    private SimpleJITLogger logger;

    private SimpleJITIRCode sjMethod;

    /** The register allocation algorithm to be used */
    private int allocator;

    private SimpleJITResultWriteFilter resultWriteFilter;
    private ResultWriter resultWriter;
    private ResultWriter codeSizeWriter;

    public SimpleJITRegisterAllocator(SimpleJITIRCode sjMethod, 
				      int allocator,
				      SimpleJITResultWriteFilter resultWriteFilter,
				      ResultWriter resultWriter,
				      ResultWriter codeSizeWriter) {
	this.sjMethod = sjMethod;
	this.blockSet = sjMethod.getBlockSet();
	this.webSet = new SimpleJITIdentitySet();
	this.logger = 
	    FULL_LOG 
	    ? new SimpleJITLogger("SJRA", System.err)
	    : new SimpleJITLogger("SJRA", LOG_SIZE);
	this.allocator = allocator;
	this.resultWriteFilter = resultWriteFilter;
	this.resultWriter = resultWriter;
	this.codeSizeWriter = codeSizeWriter;
    }

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

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

    private void run() {
	if (LOG_ENABLED) {
	    log("[RA " 
		+ sjMethod.getS3Method() + "]");
	}

	startTime = new Date();
	computeWebs();
	computeWebInterference();
	mergeWebTypeCodes_I();
//	pruneInterferencesOfDifferentType(); // This does not affect allocation
	switch(allocator) {
	case ALLOCATOR_LINEAR_PACKING:
	    allocateVirtualRegistersWithLinearPacking();
	    break;
	case ALLOCATOR_GREEDY_PACKING:
	    allocateVirtualRegistersWithGreedyPacking();
	    break;
	case ALLOCATOR_EXACT_GRAPH_COLORING:
	    allocateVirtualRegistersWithExactGraphColoring();
	    break;
	case ALLOCATOR_INCREMENTAL_GRAPH_COLORING:
	    allocateVirtualRegistersWithIncrementalGraphColoring();
	    break;
	case ALLOCATOR_GREEDY_EXACT_GRAPH_COLORING:
	    allocateVirtualRegistersWithGreedyExactGraphColoring();
	    break;
	case ALLOCATOR_GREEDY_INCREMENTAL_GRAPH_COLORING:
	    allocateVirtualRegistersWithGreedyIncrementalGraphColoring();
	    break;
	case ALLOCATOR_NORMAL_GRAPH_COLORING:
	    allocateVirtualRegistersWithNormalGraphColoring();
	    break;
	default:
	    throw new Error();
	}
	if (allocator != ALLOCATOR_NORMAL_GRAPH_COLORING) {
	    buildVirtualRegisterTable();
	    replaceDefUseWithVirtualRegister();
	    sjMethod.setVirtualRegisterTables(vregTable_I,
					      vregTable_A,
					      vregTable_J,
					      vregTable_F,
					      vregTable_D,
					      vregTable_U);
	    endTime = new Date();
	    allocationTime += endTime.getTime() - startTime.getTime();
	    sjMethod.optimizationTime += allocationTime;
	    if (resultWriteFilter.shouldWrite(sjMethod))
		writeResults();
	} else { // When ALLOCATOR_NORMAL_GRAPH_COLORING
	    endTime = new Date();
	    allocationTime += endTime.getTime() - startTime.getTime();
	    sjMethod.optimizationTime += allocationTime;
	    if (resultWriteFilter.shouldWrite(sjMethod)) {
		cutTailOfnormalGCNReg2TotalSpillCosts();
		writeResultsForNormalGraphColoring();
	    }
	}
	printEstimatedCodeSize();
    }

    public void print() {
	if (LOG_ENABLED) {
	    printBlockSet();
	    printWebSet();
	    printMergedWebSetSet();
	    //printMergedWebSetSetAsVirtualRegisters();
	    printVirtualRegisterTable();
	}
    }

    public void printBlockSet() {
	String b = blockSet.toString();
	log(b);
    }

    private void printEstimatedCodeSize() {
	if (WRITE_CODESIZE)
	    codeSizeWriter.println(sjMethod.getBytecodeSize() + " " 
				   + sjMethod.estimateIRCodeSize());
    }

    public void printWebSet() {
	String r = "Web list :\n";
	SimpleJITWebTemporaryOperand[] webs = 
	    new SimpleJITWebTemporaryOperand[webSet.size()];
	webSet.toArray(webs);
	sortWebs(webs);
	for(int i = webs.length - 1; i >= 0; i--) {
	    SimpleJITWebTemporaryOperand w = webs[i];
	    r += w.toString() + " : wrc = " 
		+ w.getWeightedRefCount() + " : "
		+ w.getInterferersAsString() + "\n";
	}
	log(r);
    }

    public void printMergedWebSetSet() {
	printMergedWebSetSet_helper(TypeCodes.INT);
	printMergedWebSetSet_helper(TypeCodes.REFERENCE);
	printMergedWebSetSet_helper(TypeCodes.LONG);
	printMergedWebSetSet_helper(TypeCodes.FLOAT);
	printMergedWebSetSet_helper(TypeCodes.DOUBLE);
	printMergedWebSetSet_helper(TypeCodes.RETURNADDRESS);
    }

    private void printMergedWebSetSet_helper(char typeCode) {
	String r = "Merged web set list " + typeCode + " :\n";
	SimpleJITIdentitySet mergedWebSetSet =
	    typeCode2MergedWebSetSet(typeCode);
	SimpleJITWebTemporaryOperand.Set[] webSets = 
	    new SimpleJITWebTemporaryOperand.Set
	    [mergedWebSetSet.size()];
	mergedWebSetSet.toArray(webSets);
	sortWebSets(webSets);
	for(int i = webSets.length - 1; i >= 0; i--) {
	    SimpleJITWebTemporaryOperand.Set ws = webSets[i];
	    r += ws.toString() + " : wrc = " 
		+ ws.getWeightedRefCount() + " : "
		+ ws.getInterferersAsString() + "\n";
	}
	log(r);
    }

    public void printMergedWebSetSetAsVirtualRegisters() {
	printMergedWebSetSetAsVirtualRegisters_helper(TypeCodes.INT);
	printMergedWebSetSetAsVirtualRegisters_helper(TypeCodes.REFERENCE);
	printMergedWebSetSetAsVirtualRegisters_helper(TypeCodes.LONG);
	printMergedWebSetSetAsVirtualRegisters_helper(TypeCodes.FLOAT);
	printMergedWebSetSetAsVirtualRegisters_helper(TypeCodes.DOUBLE);
	printMergedWebSetSetAsVirtualRegisters_helper(TypeCodes.RETURNADDRESS);
    }

    private void printMergedWebSetSetAsVirtualRegisters_helper
	(char typeCode) {
	String r = "Virtual register list " + typeCode + " :\n";
	SimpleJITIdentitySet mergedWebSetSet =
	    typeCode2MergedWebSetSet(typeCode);
	SimpleJITWebTemporaryOperand.Set[] webSets = 
	    new SimpleJITWebTemporaryOperand.Set
	    [mergedWebSetSet.size()];
	mergedWebSetSet.toArray(webSets);
	sortWebSets(webSets);
	for(int i = webSets.length - 1; i >= 0; i--) {
	    SimpleJITWebTemporaryOperand.Set ws = webSets[i];
	    r += ws.toStringAsVirtualRegister() + " : wrc = " 
		+ ws.getWeightedRefCount() + " : "
		+ ws.getInterferersAsVirtualRegisterAsString() + "\n";
	}
	log(r);
    }

    public void printVirtualRegisterTable() {
	printVirtualRegisterTable_helper(TypeCodes.INT);
	printVirtualRegisterTable_helper(TypeCodes.REFERENCE);
	printVirtualRegisterTable_helper(TypeCodes.LONG);
	printVirtualRegisterTable_helper(TypeCodes.FLOAT);
	printVirtualRegisterTable_helper(TypeCodes.DOUBLE);
	printVirtualRegisterTable_helper(TypeCodes.RETURNADDRESS);
    }

    private void printVirtualRegisterTable_helper(char typeCode) {
	String r = "Virtual register list " + typeCode + " :\n";
	SimpleJITVirtualRegisterOperand[] vregTable =
	    typeCode2VRegTable(typeCode);
	for(int i = 0; i < vregTable.length; i++) {
	    SimpleJITVirtualRegisterOperand v = vregTable[i];
	    r += v.toString() + " : wrc = " 
		+ v.getWeightedRefCount() + " : "
		+ v.getInterferersAsString() + "\n";
	}
	log(r);
    }

    /**
     * Build the virtual register tables from the merged web set set.
     */
    public void buildVirtualRegisterTable() {
	buildVirtualRegisterTable_helper(TypeCodes.INT);
	buildVirtualRegisterTable_helper(TypeCodes.REFERENCE);
	buildVirtualRegisterTable_helper(TypeCodes.LONG);
	buildVirtualRegisterTable_helper(TypeCodes.FLOAT);
	buildVirtualRegisterTable_helper(TypeCodes.DOUBLE);
	buildVirtualRegisterTable_helper(TypeCodes.RETURNADDRESS);
    }

    private void buildVirtualRegisterTable_helper(char typeCode) {
	SimpleJITIdentitySet mergedWebSetSet =
	    typeCode2MergedWebSetSet(typeCode);
	SimpleJITWebTemporaryOperand.Set[] webSets = 
	    new SimpleJITWebTemporaryOperand.Set
	    [mergedWebSetSet.size()];
	mergedWebSetSet.toArray(webSets);
	sortWebSets(webSets);
	SimpleJITVirtualRegisterOperand[] vregTable =
	    allocateVRegTable(typeCode, webSets.length);
	int vreg_index = 0;
	for(int i = webSets.length - 1; i >= 0; i--) {
	    SimpleJITWebTemporaryOperand.Set ws = webSets[i];
	    SimpleJITVirtualRegisterOperand vreg =
		ws.getVirtualRegister();
	    vreg.setWeightedRefCount(ws.getWeightedRefCount());
	    vreg.setInterferers(ws.getInterferersAsVirtualRegisters());
	    vregTable[vreg_index] = vreg;
	    vreg_index++;
	}
    }

    private void writeResults() {
	String output = "";
	S3Method m = sjMethod.getS3Method();
	String mname = m.getSelector().toString();
	String tcname = m.getDeclaringType().getContext().toString();
	output += tcname + "\n" + mname + "\n";
	output = writeResults_helper(TypeCodes.INT, output);
	output = writeResults_helper(TypeCodes.REFERENCE, output);
	output = writeResults_helper(TypeCodes.LONG, output);
	output = writeResults_helper(TypeCodes.FLOAT, output);
	output = writeResults_helper(TypeCodes.DOUBLE, output);
	output = writeResults_helper(TypeCodes.RETURNADDRESS, output);
	output += "\n" + (sjMethod.optimizationTime) + "\n";
	resultWriter.println(output);
    }

    private String writeResults_helper(char typeCode, String output) {
	output += typeCode + "\n";
	SimpleJITVirtualRegisterOperand[] vregTable =
	    typeCode2VRegTable(typeCode);
	output += vregTable.length + "\n";
	for(int i = 0; i < vregTable.length; i++) {
	    SimpleJITVirtualRegisterOperand v = vregTable[i];
	    output += "\t" + v.getWeightedRefCount() + "\n";
	}
	return output;
    }

    private SimpleJITIdentitySet 
	typeCode2MergedWebSetSet(char typeCode) {
	switch(typeCode) {
	case TypeCodes.INT:
	    return mergedWebSetSet_I;
	case TypeCodes.REFERENCE:
	    return mergedWebSetSet_A;
	case TypeCodes.FLOAT:
	    return mergedWebSetSet_F;
	case TypeCodes.LONG:
	    return mergedWebSetSet_D;
	case TypeCodes.DOUBLE:
	    return mergedWebSetSet_J;
	case TypeCodes.RETURNADDRESS:
	    return mergedWebSetSet_U;
	default:
	    throw new Error("unexpected " + typeCode);
	}
    }
    private void resetMergedWebSetSetOnType(char typeCode) {
	switch(typeCode) {
	case TypeCodes.INT:
	    mergedWebSetSet_I = new SimpleJITIdentitySet();
	    break;
	case TypeCodes.REFERENCE:
	    mergedWebSetSet_A = new SimpleJITIdentitySet();
	    break;
	case TypeCodes.FLOAT:
	    mergedWebSetSet_F = new SimpleJITIdentitySet();
	    break;
	case TypeCodes.LONG:
	    mergedWebSetSet_D = new SimpleJITIdentitySet();
	    break;
	case TypeCodes.DOUBLE:
	    mergedWebSetSet_J = new SimpleJITIdentitySet();
	    break;
	case TypeCodes.RETURNADDRESS:
	    mergedWebSetSet_U = new SimpleJITIdentitySet();
	    break;
	default:
	    throw new Error();
	}
    }




    private SimpleJITVirtualRegisterOperand[]
	typeCode2VRegTable(char typeCode) {
	switch(typeCode) {
	case TypeCodes.INT:
	    return vregTable_I;
	case TypeCodes.REFERENCE:
	    return vregTable_A;
	case TypeCodes.FLOAT:
	    return vregTable_F;
	case TypeCodes.LONG:
	    return vregTable_J;
	case TypeCodes.DOUBLE:
	    return vregTable_D;
	case TypeCodes.RETURNADDRESS:
	    return vregTable_U;
	default:
	    throw new Error("unexpected " + typeCode);
	}
    }
    private SimpleJITVirtualRegisterOperand[] 
	allocateVRegTable(char typeCode, int length) {
	switch(typeCode) {
	case TypeCodes.INT:
	    vregTable_I = new SimpleJITVirtualRegisterOperand[length];
	    return vregTable_I;
	case TypeCodes.REFERENCE:
	    vregTable_A = new SimpleJITVirtualRegisterOperand[length];
	    return vregTable_A;
	case TypeCodes.FLOAT:
	    vregTable_F = new SimpleJITVirtualRegisterOperand[length];
	    return vregTable_F;
	case TypeCodes.LONG:
	    vregTable_J = new SimpleJITVirtualRegisterOperand[length];
	    return vregTable_J;
	case TypeCodes.DOUBLE:
	    vregTable_D = new SimpleJITVirtualRegisterOperand[length];
	    return vregTable_D;
	case TypeCodes.RETURNADDRESS:
	    vregTable_U = new SimpleJITVirtualRegisterOperand[length];
	    return vregTable_U;
	default:
	    throw new Error();
	}
    }

    /**
     * Merge webs based on the conversion
     * A, I, U -> I
     * F, D -> F
     * J    -> J
     */
    private void mergeWebTypeCodes_IFJ() {
	SimpleJITWebTemporaryOperand[] webs =
	    new SimpleJITWebTemporaryOperand[webSet.size()];
	webSet.toArray(webs);
	for(int i = 0; i < webs.length; i++) {
	    SimpleJITWebTemporaryOperand w = webs[i];
	    switch(w.getTypeCode()) {
	    case TypeCodes.REFERENCE:
	    case TypeCodes.RETURNADDRESS:
		w.setTypeCode(TypeCodes.INT);
		break;
	    case TypeCodes.DOUBLE:
		w.setTypeCode(TypeCodes.FLOAT);
		break;
	    default:
		
	    }
	}
    }

    /**
     * Merge webs based on the conversion
     * A, I, U, F, D, J -> I
     */
    private void mergeWebTypeCodes_I() {
	SimpleJITWebTemporaryOperand[] webs =
	    new SimpleJITWebTemporaryOperand[webSet.size()];
	webSet.toArray(webs);
	for(int i = 0; i < webs.length; i++) {
	    SimpleJITWebTemporaryOperand w = webs[i];
	    w.setTypeCode(TypeCodes.INT);
	}
    }

    /**
     * Remove from each web's interference list webs of different
     * types.
     */
    private void pruneInterferencesOfDifferentType() {
	for(Iterator it = webSet.iterator();
	    it.hasNext(); ) {
	    SimpleJITWebTemporaryOperand w =
		(SimpleJITWebTemporaryOperand)it.next();
	    char typeCode = w.getTypeCode();
	    for(Iterator it1 = w.getInterferers().iterator();
		it1.hasNext(); ) {
		SimpleJITWebTemporaryOperand w1 =
		    (SimpleJITWebTemporaryOperand)it1.next();
		if (w1.getTypeCode() != typeCode)
		    w.removeInterferer(w1);
	    }
	}
    }

    /**
     * Register allocation based on weighted reference counts and
     * a simple greedy packing.
     */
    private void allocateVirtualRegistersWithGreedyPacking() {
	SimpleJITWebTemporaryOperand[] webs =
	    new SimpleJITWebTemporaryOperand[webSet.size()];
	webSet.toArray(webs);
	sortWebs(webs);
	HashMap webReplaceMap = new HashMap();

	resetMergedWebSetSetOnType(TypeCodes.INT);
	resetMergedWebSetSetOnType(TypeCodes.REFERENCE);
	resetMergedWebSetSetOnType(TypeCodes.LONG);
	resetMergedWebSetSetOnType(TypeCodes.FLOAT);
	resetMergedWebSetSetOnType(TypeCodes.DOUBLE);
	resetMergedWebSetSetOnType(TypeCodes.RETURNADDRESS);

	// Pack (aggresive) O(n^2)
	for(int i = webs.length - 1; i >= 0; i--) {
	    SimpleJITWebTemporaryOperand wi = webs[i];
	    if (wi == null)
		continue;
	    SimpleJITWebTemporaryOperand.Set mwebSet = 
		new SimpleJITWebTemporaryOperand.Set(wi.getTypeCode());
	    mwebSet.add(wi);
	    for(int j = i - 1; j >= 0; j--) {
		SimpleJITWebTemporaryOperand wj = webs[j];
		if (wj == null)
		    continue;
		if (mwebSet.getTypeCode() == wj.getTypeCode()
		    && ! mwebSet.getInterferers().contains(wj)) {
		    //d("merging " + wi + " and " + wj);
		    mwebSet.add(wj);
		    webs[j] = null;
		}
	    }
	    typeCode2MergedWebSetSet(wi.getTypeCode()).add(mwebSet);
	}

	allocateVirtualRegistersWithGreedyPacking_helper(TypeCodes.INT);
	allocateVirtualRegistersWithGreedyPacking_helper(TypeCodes.FLOAT);
	allocateVirtualRegistersWithGreedyPacking_helper(TypeCodes.LONG);
	allocateVirtualRegistersWithGreedyPacking_helper(TypeCodes.DOUBLE);
	allocateVirtualRegistersWithGreedyPacking_helper(TypeCodes.REFERENCE);
	allocateVirtualRegistersWithGreedyPacking_helper(TypeCodes.RETURNADDRESS);
    }

    private void allocateVirtualRegistersWithGreedyPacking_helper
	(char typeCode) {
	SimpleJITIdentitySet mergedWebSetSet = 
	    typeCode2MergedWebSetSet(typeCode);
	// Allocate virtual registers
	SimpleJITWebTemporaryOperand.Set[] mergedWebSets =
	    new SimpleJITWebTemporaryOperand.Set
	    [mergedWebSetSet.size()];
	mergedWebSetSet.toArray(mergedWebSets);

	sortWebSets(mergedWebSets);

	int regCounter = 0;
	for(int i = mergedWebSets.length - 1; i >= 0; i--) {
	    SimpleJITWebTemporaryOperand.Set webset = mergedWebSets[i];
	    if (webset.getTypeCode() != typeCode)
		throw new Error();
	    SimpleJITVirtualRegisterOperand reg = 
		vrf.create(webset.getTypeCode(), regCounter);
	    webset.setVirtualRegister(reg);
	    regCounter++;
	}

    }


    /**
     * The driver for no-spill graph coloring register allocation
     * based on optimistic coloring.
     */
    private void allocateVirtualRegistersWithExactGraphColoring() {
	allocateVirtualRegistersWithExactGraphColoringFor(TypeCodes.INT);
	allocateVirtualRegistersWithExactGraphColoringFor(TypeCodes.REFERENCE);
	allocateVirtualRegistersWithExactGraphColoringFor(TypeCodes.LONG);
	allocateVirtualRegistersWithExactGraphColoringFor(TypeCodes.FLOAT);
	allocateVirtualRegistersWithExactGraphColoringFor(TypeCodes.DOUBLE);
	allocateVirtualRegistersWithExactGraphColoringFor(TypeCodes.RETURNADDRESS);
    }
    private void allocateVirtualRegistersWithExactGraphColoringFor
	(char typeCode) {
	boolean success = false;
	boolean success_ever = false;
	int nweb = 0;
	for(Iterator it = webSet.iterator();
	    it.hasNext(); ) {
	    SimpleJITWebTemporaryOperand w =
		(SimpleJITWebTemporaryOperand)it.next();
	    if (w.getTypeCode() == typeCode)
		nweb++;
	}

	nweb++; // for a complete interference graph, this is needed...

	//d("nweb = " + nweb);
	if (nweb < 4) { // linear search
	    for(int i = nweb; i >= 0; i--) {
		success =
		    allocateVirtualRegistersWithExactGraphColoringFor
		    (typeCode, i);
		success_ever |= success;
		if (! success)
		    break;
	    }
	} else { // binary search
	    int high = nweb;
	    int low  = 0;
	    while (high - low > 1) {
		//d("high = " + high + ", low = " + low);
		int medium = (high + low) / 2;
		//d("trying medium = " + medium);
		success = 
		    allocateVirtualRegistersWithExactGraphColoringFor
		    (typeCode, medium);
		success_ever |= success;
		//d("success = " + success);
		if (success)
		    high = medium;
		else
		    low = medium;
	    }
	}

	if (! success_ever)
	    throw new Error("Register allocation failed");

	sortAndRenameMergedWebSetSet(typeCode);
    }

    private void sortAndRenameMergedWebSetSet(char typeCode) {
	SimpleJITIdentitySet mergedWebSetSet =
	    typeCode2MergedWebSetSet(typeCode);
	SimpleJITWebTemporaryOperand.Set[] webSets = 
	    new SimpleJITWebTemporaryOperand.Set
	    [mergedWebSetSet.size()];
	mergedWebSetSet.toArray(webSets);
	sortWebSets(webSets);
	for(int i = webSets.length - 1, j = 0; i >= 0; i--, j++) {
	    SimpleJITWebTemporaryOperand.Set ws = webSets[i];
	    SimpleJITVirtualRegisterOperand vreg =
		ws.getVirtualRegister();
	    vrf.swapVirtualRegisterIndices(typeCode, j, vreg.getIndex());
	}
    }

    private SimpleJITWebTemporaryOperand findSimplifiableWeb
	(SimpleJITIdentitySet webs, int K) {
	for(Iterator it = webs.iterator();
	    it.hasNext(); ) {
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    if (w.getScratchInterferers().size() < K)
		return w;
	}
	return null;
    }

    private void d(String m) {
	log(m);
    }

    /**
     * Apply no-spill graph coloring register allocation based on
     * optimistic coloring with K virtual registers of the given type.
     * @param typeCode the type
     * @param K the number of given virtual registers
     * @return true if the allocation performed without spills, false
     * otherwise.
     */
    private boolean allocateVirtualRegistersWithExactGraphColoringFor
	(char typeCode,
	 int K) {
	SimpleJITStack webStack = new SimpleJITStack();

	// Prepare virtual registers
	SimpleJITVirtualRegisterOperand[] vregs =
	    new SimpleJITVirtualRegisterOperand[K];
	// The map from a virtual reg to the web set
	IdentityHashMap vreg2WebSet = new IdentityHashMap();
	for(int i = 0; i < vregs.length; i++) {
	    vregs[i] = vrf.create(typeCode, i);
	}
	
	SimpleJITIdentitySet igraph = webSet.copy();

	// Initialize scratch interference lists and reset virtual
	// registers in webs.
	for(Iterator it = igraph.iterator();
	    it.hasNext(); ) { 
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    // Prune webs of wrong types
	    if (w.getTypeCode() != typeCode) {
		it.remove();
	    } else {
		w.copyInterferersToScatch();
		w.setScratchVirtualRegister(null);
	    }
	}

	// SIMPLIFY and SPILL phases
	while (! igraph.empty()) {
	    SimpleJITWebTemporaryOperand simplifiable = 
		findSimplifiableWeb(igraph, vregs.length);
	    if (simplifiable != null) {
		// SIMPLIFY
		simplifiable.setPotentialSpillFlag(false);
		webStack.push(simplifiable);
		igraph.remove(simplifiable);
		// Remove interference edges
		for(Iterator it = igraph.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w = 
			(SimpleJITWebTemporaryOperand)it.next();
		    w.removeScatchInterferer(simplifiable);
		}
	    } else {
		// SPILL
 		// search for the smallest spillcost/degree
		SimpleJITWebTemporaryOperand potentialSpill = null;
 		for(Iterator it = igraph.iterator();
 		    it.hasNext(); ) {
 		    SimpleJITWebTemporaryOperand w =
 			(SimpleJITWebTemporaryOperand)it.next();
		    if (potentialSpill == null) {
			potentialSpill = w;
 		    } else if ((w.getWeightedRefCount() 
				* potentialSpill.getScratchInterferers().size())
			       < (potentialSpill.getWeightedRefCount()
				  * w.getScratchInterferers().size())) {
 			potentialSpill = w;
 		    }
 		}
		igraph.remove(potentialSpill);

		if (potentialSpill == null
		    || potentialSpill.getScratchInterferers()
		    .size() < vregs.length) {
		    throw new Error();
		}
		potentialSpill.setPotentialSpillFlag(true);
		webStack.push(potentialSpill);
		igraph.remove(potentialSpill);
		// Remove interference edges
		for(Iterator it = igraph.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w = 
			(SimpleJITWebTemporaryOperand)it.next();
		    w.removeScatchInterferer(potentialSpill);
		}
	    }
	}

	// igraph must be empty here

	if (! igraph.empty())
	    throw new Error();

	// SELECT phase
	while (! webStack.empty()) {
	    SimpleJITWebTemporaryOperand w =
		(SimpleJITWebTemporaryOperand)webStack.pop();
	    igraph.add(w);
	    for(Iterator it = igraph.copy().iterator();
		// Copy igraph. Don't run more than one iterator on
		// the same set
		it.hasNext(); ) {
		SimpleJITWebTemporaryOperand wi =
		    (SimpleJITWebTemporaryOperand)it.next();
		wi.copyInterferersToScatchForSubset(igraph);
	    }
	    SimpleJITIdentitySet scratchInterferers =
		w.getScratchInterferers();

	    SimpleJITIdentitySet usedVRegs = 
		new SimpleJITIdentitySet();
	    for(Iterator it = scratchInterferers.iterator();
		it.hasNext(); ) {
		SimpleJITWebTemporaryOperand wi =
		    (SimpleJITWebTemporaryOperand)it.next();
		SimpleJITVirtualRegisterOperand vr = 
		    wi.getScratchVirtualRegister();
		if (vr != null)
		    usedVRegs.add(vr);
	    }
	    SimpleJITIdentitySet vregSet = 
		new SimpleJITIdentitySet(vregs);
	    vregSet.diff(usedVRegs);
	    if (vregSet.size() > 0) { // found a color
		SimpleJITVirtualRegisterOperand vreg =
		    (SimpleJITVirtualRegisterOperand)vregSet.remove();

		w.setScratchVirtualRegister(vreg);

		// Put the mapping from the web to the register into
		// the hash map
		SimpleJITWebTemporaryOperand.Set vreg_wset =
		    (SimpleJITWebTemporaryOperand.Set)
		    vreg2WebSet.get(vreg);
		if (vreg_wset == null) {
		    vreg_wset = 
			new SimpleJITWebTemporaryOperand.Set
			(w.getTypeCode());
		    vreg2WebSet.put(vreg, vreg_wset);
		}
		vreg_wset.add(w);
	    } else { // actual spill
		return false;
	    }
	}

	// Allocation succeeded

	// Assign vregs to webs and set mergedWebSetSet
	resetMergedWebSetSetOnType(typeCode);
	for(int i = 0; i < vregs.length; i++) {
	    SimpleJITWebTemporaryOperand.Set webset =
		(SimpleJITWebTemporaryOperand.Set)
		vreg2WebSet.get(vregs[i]);
	    if (webset != null) {
		for(Iterator it = webset.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w =
			(SimpleJITWebTemporaryOperand)
			it.next();
		    w.setVirtualRegister(vregs[i]);
		}
		typeCode2MergedWebSetSet(typeCode)
		    .add(webset);
	    }
	}

	return true;
    }



    /**
     * The driver for incremental graph coloring register allocation
     * based on optimistic coloring.
     */
    private void allocateVirtualRegistersWithIncrementalGraphColoring() {
	allocateVirtualRegistersWithIncrementalGraphColoringFor(TypeCodes.INT);
	allocateVirtualRegistersWithIncrementalGraphColoringFor(TypeCodes.REFERENCE);
	allocateVirtualRegistersWithIncrementalGraphColoringFor(TypeCodes.LONG);
	allocateVirtualRegistersWithIncrementalGraphColoringFor(TypeCodes.FLOAT);
	allocateVirtualRegistersWithIncrementalGraphColoringFor(TypeCodes.DOUBLE);
	allocateVirtualRegistersWithIncrementalGraphColoringFor(TypeCodes.RETURNADDRESS);
    }
    private void allocateVirtualRegistersWithIncrementalGraphColoringFor
	(char typeCode) {
	boolean progress = true;
	int regCounter = 0;
	SimpleJITIdentitySet igraph = webSet.copy();

	resetMergedWebSetSetOnType(typeCode);

	// Initialize scratch interference lists and reset virtual
	// registers in webs.
	for(Iterator it = igraph.iterator();
	    it.hasNext(); ) { 
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    // Prune webs of wrong types
	    if (w.getTypeCode() != typeCode) {
		it.remove();
	    } else {
		w.setScratchVirtualRegister(null);
	    }
	}
	while (! igraph.empty()) {
		allocateVirtualRegistersWithIncrementalGraphColoringFor
		(typeCode, regCounter, 1, igraph);
	    regCounter++;
	}

	sortAndRenameMergedWebSetSet(typeCode);
    }

    /**
     * Apply incremental graph coloring register allocation based on
     * optimistic coloring with nreg virtual register of the given
     * type.  Try allocate nreg register to the most important web,
     * but will leave other webs.
     * @param typeCode the type
     * @param start_reg the virtual register to start allocation with
     * @param nreg the number of the virtual register starting at start_reg
     */
    private void allocateVirtualRegistersWithIncrementalGraphColoringFor
	(char typeCode,
	 int start_reg,
	 int nreg,
	 SimpleJITIdentitySet igraph) {
	SimpleJITStack webStack = new SimpleJITStack();

	// Prepare virtual registers
	SimpleJITVirtualRegisterOperand[] vregs =
	    new SimpleJITVirtualRegisterOperand[nreg];
	// The map from a virtual reg to the web set
	IdentityHashMap vreg2WebSet = new IdentityHashMap();
	for(int i = 0; i < nreg; i++) {
	    vregs[i] = vrf.create(typeCode, start_reg + i);
	}
	
	// Initialize scratch interference lists
	for(Iterator it = igraph.copy().iterator();
	    it.hasNext(); ) { 
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    if (w.getTypeCode() != typeCode) {
		throw new Error();
	    } else {
		w.copyInterferersToScatchForSubset(igraph);
	    }
	}

	// SIMPLIFY and SPILL phases
	while (! igraph.empty()) {
	    SimpleJITWebTemporaryOperand simplifiable = 
		findSimplifiableWeb(igraph, vregs.length);
	    if (simplifiable != null) {
		// SIMPLIFY
		simplifiable.setPotentialSpillFlag(false);
		webStack.push(simplifiable);
		igraph.remove(simplifiable);
		// Remove interference edges
		for(Iterator it = igraph.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w = 
			(SimpleJITWebTemporaryOperand)it.next();
		    w.removeScatchInterferer(simplifiable);
		}
	    } else {
		// SPILL
 		// search for the smallest spillcost/degree
		SimpleJITWebTemporaryOperand potentialSpill = null;
 		for(Iterator it = igraph.iterator();
 		    it.hasNext(); ) {
 		    SimpleJITWebTemporaryOperand w =
 			(SimpleJITWebTemporaryOperand)it.next();
		    if (potentialSpill == null) {
			potentialSpill = w;
 		    } else if ((w.getWeightedRefCount() 
				* potentialSpill.getScratchInterferers().size())
			       < (potentialSpill.getWeightedRefCount()
				  * w.getScratchInterferers().size())) {
 			potentialSpill = w;
 		    }
 		}
		igraph.remove(potentialSpill);
		if (potentialSpill == null
		    || potentialSpill.getScratchInterferers()
		    .size() < vregs.length) {
		    throw new Error();
		}
		potentialSpill.setPotentialSpillFlag(true);
		webStack.push(potentialSpill);
		igraph.remove(potentialSpill);
		// Remove interference edges
		for(Iterator it = igraph.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w = 
			(SimpleJITWebTemporaryOperand)it.next();
		    w.removeScatchInterferer(potentialSpill);
		}
	    }
	}

	// igraph must be empty here

	if (! igraph.empty())
	    throw new Error();

	// SELECT phase
	while (! webStack.empty()) {
	    SimpleJITWebTemporaryOperand w =
		(SimpleJITWebTemporaryOperand)webStack.pop();
	    igraph.add(w);
	    for(Iterator it = igraph.copy().iterator();
		it.hasNext(); ) {
		SimpleJITWebTemporaryOperand wi =
		    (SimpleJITWebTemporaryOperand)it.next();
		wi.copyInterferersToScatchForSubset(igraph);
	    }
	    SimpleJITIdentitySet scratchInterferers =
		w.getScratchInterferers();

	    SimpleJITIdentitySet usedVRegs = 
		new SimpleJITIdentitySet();
	    for(Iterator it = scratchInterferers.iterator();
		it.hasNext(); ) {
		SimpleJITWebTemporaryOperand wi =
		    (SimpleJITWebTemporaryOperand)it.next();
		SimpleJITVirtualRegisterOperand vr = 
		    wi.getScratchVirtualRegister();
		if (vr != null)
		    usedVRegs.add(vr);
	    }
	    SimpleJITIdentitySet vregSet = 
		new SimpleJITIdentitySet(vregs);
	    vregSet.diff(usedVRegs);
	    if (vregSet.size() > 0) { // found a color
		SimpleJITVirtualRegisterOperand vreg =
		    (SimpleJITVirtualRegisterOperand)vregSet.remove();

		w.setScratchVirtualRegister(vreg);

		// Put the mapping from the web to the register into
		// the hash map
		SimpleJITWebTemporaryOperand.Set vreg_wset =
		    (SimpleJITWebTemporaryOperand.Set)
		    vreg2WebSet.get(vreg);
		if (vreg_wset == null) {
		    vreg_wset = 
			new SimpleJITWebTemporaryOperand.Set
			(w.getTypeCode());
		    vreg2WebSet.put(vreg, vreg_wset);
		}
		vreg_wset.add(w);
	    }
	    // keep going even if spill heuristic fails
	}

	// Assign vregs to webs and set mergedWebSetSet
	for(int i = 0; i < vregs.length; i++) {
	    SimpleJITWebTemporaryOperand.Set webset =
		(SimpleJITWebTemporaryOperand.Set)
		vreg2WebSet.get(vregs[i]);
	    if (webset != null) {
		for(Iterator it = webset.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w =
			(SimpleJITWebTemporaryOperand)
			it.next();
		    w.setVirtualRegister(vregs[i]);
		    igraph.remove(w); // remove the web from igraph
		}
		typeCode2MergedWebSetSet(typeCode)
		    .add(webset);
	    }
	}
    }

    private SimpleJITWebTemporaryOperand findSimplifiableWebGreedily
	(SimpleJITIdentitySet webs, int K) {
	SimpleJITWebTemporaryOperand whigh = null;
	for(Iterator it = webs.iterator();
	    it.hasNext(); ) {
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    if (w.getScratchInterferers().size() < K) {
		if (whigh == null)
		    whigh = w;
		else if (w.getWeightedRefCount() > whigh.getWeightedRefCount())
		    whigh = w;
	    }
	}
	return whigh;
    }

    private SimpleJITWebTemporaryOperand findSimplifiableWebHumbly
	(SimpleJITIdentitySet webs, int K) {
	SimpleJITWebTemporaryOperand wlow = null;
	for(Iterator it = webs.iterator();
	    it.hasNext(); ) {
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    if (w.getScratchInterferers().size() < K) {
		if (wlow == null)
		    wlow = w;
		else if (w.getWeightedRefCount() < wlow.getWeightedRefCount())
		    wlow = w;
	    }
	}
	return wlow;
    }

    private SimpleJITWebTemporaryOperand findAnyWebGreedily
	(SimpleJITIdentitySet webs) {
	SimpleJITWebTemporaryOperand whigh = null;
	for(Iterator it = webs.iterator();
	    it.hasNext(); ) {
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    if (whigh == null)
		whigh = w;
	    else if (w.getWeightedRefCount() < whigh.getWeightedRefCount())
		whigh = w;
	}
	return whigh;
    }

    private SimpleJITWebTemporaryOperand findAnyWebHumbly
	(SimpleJITIdentitySet webs) {
	SimpleJITWebTemporaryOperand wlow = null;
	for(Iterator it = webs.iterator();
	    it.hasNext(); ) {
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    if (wlow == null)
		wlow = w;
	    else if (w.getWeightedRefCount() < wlow.getWeightedRefCount())
		wlow = w;
	}
	return wlow;
    }

    /**
     * The driver for incremental graph coloring register allocation
     * based on optimistic coloring.
     */
    private void allocateVirtualRegistersWithGreedyIncrementalGraphColoring() {
	allocateVirtualRegistersWithGreedyIncrementalGraphColoringFor(TypeCodes.INT);
	allocateVirtualRegistersWithGreedyIncrementalGraphColoringFor(TypeCodes.REFERENCE);
	allocateVirtualRegistersWithGreedyIncrementalGraphColoringFor(TypeCodes.LONG);
	allocateVirtualRegistersWithGreedyIncrementalGraphColoringFor(TypeCodes.FLOAT);
	allocateVirtualRegistersWithGreedyIncrementalGraphColoringFor(TypeCodes.DOUBLE);
	allocateVirtualRegistersWithGreedyIncrementalGraphColoringFor(TypeCodes.RETURNADDRESS);
    }
    private void allocateVirtualRegistersWithGreedyIncrementalGraphColoringFor
	(char typeCode) {
	boolean progress = true;
	int regCounter = 0;
	SimpleJITIdentitySet igraph = webSet.copy();

	resetMergedWebSetSetOnType(typeCode);

	// Initialize scratch interference lists and reset virtual
	// registers in webs.
	for(Iterator it = igraph.iterator();
	    it.hasNext(); ) { 
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    // Prune webs of wrong types
	    if (w.getTypeCode() != typeCode) {
		it.remove();
	    } else {
		w.setScratchVirtualRegister(null);
	    }
	}
	while (! igraph.empty()) {
		allocateVirtualRegistersWithGreedyIncrementalGraphColoringFor
		(typeCode, regCounter, 1, igraph);
	    regCounter++;
	}

	sortAndRenameMergedWebSetSet(typeCode);
    }

    /**
     * Apply incremental graph coloring register allocation based on
     * optimistic coloring with nreg virtual register of the given
     * type.  Try allocate nreg register to the most important web,
     * but will leave other webs.
     * @param typeCode the type
     * @param start_reg the virtual register to start allocation with
     * @param nreg the number of the virtual register starting at start_reg
     */
    private void allocateVirtualRegistersWithGreedyIncrementalGraphColoringFor
	(char typeCode,
	 int start_reg,
	 int nreg,
	 SimpleJITIdentitySet igraph) {
	SimpleJITStack webStack = new SimpleJITStack();

	// Prepare virtual registers
	SimpleJITVirtualRegisterOperand[] vregs =
	    new SimpleJITVirtualRegisterOperand[nreg];
	// The map from a virtual reg to the web set
	IdentityHashMap vreg2WebSet = new IdentityHashMap();
	for(int i = 0; i < nreg; i++) {
	    vregs[i] = vrf.create(typeCode, start_reg + i);
	}
	
	// Initialize scratch interference lists
	for(Iterator it = igraph.copy().iterator();
	    it.hasNext(); ) { 
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    if (w.getTypeCode() != typeCode) {
		throw new Error();
	    } else {
		w.copyInterferersToScatchForSubset(igraph);
	    }
	}

	// SIMPLIFY and SPILL phases
	while (! igraph.empty()) {
	    SimpleJITWebTemporaryOperand simplifiable = 
		//findSimplifiableWebGreedily(igraph, vregs.length);
		findSimplifiableWebHumbly(igraph, vregs.length);
	    //findSimplifiableWeb(igraph, vregs.length);
	    if (simplifiable != null) {
		// SIMPLIFY
		simplifiable.setPotentialSpillFlag(false);
		webStack.push(simplifiable);
		igraph.remove(simplifiable);
		// Remove interference edges
		for(Iterator it = igraph.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w = 
			(SimpleJITWebTemporaryOperand)it.next();
		    w.removeScatchInterferer(simplifiable);
		}
	    } else {
		// SPILL
 		// search for the smallest spillcost/degree
		SimpleJITWebTemporaryOperand potentialSpill = null;
 		for(Iterator it = igraph.iterator();
 		    it.hasNext(); ) {
 		    SimpleJITWebTemporaryOperand w =
 			(SimpleJITWebTemporaryOperand)it.next();
		    if (potentialSpill == null) {
			potentialSpill = w;
 		    } else if ((w.getWeightedRefCount() 
				* potentialSpill.getScratchInterferers().size())
			       < (potentialSpill.getWeightedRefCount()
				  * w.getScratchInterferers().size())) {
 			potentialSpill = w;
 		    }
 		}
		igraph.remove(potentialSpill);

		if (potentialSpill == null
		    || potentialSpill.getScratchInterferers()
		    .size() < vregs.length) {
		    throw new Error();
		}
		potentialSpill.setPotentialSpillFlag(true);
		webStack.push(potentialSpill);
		igraph.remove(potentialSpill);
		// Remove interference edges
		for(Iterator it = igraph.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w = 
			(SimpleJITWebTemporaryOperand)it.next();
		    w.removeScatchInterferer(potentialSpill);
		}
	    }
	}

	// igraph must be empty here

	if (! igraph.empty())
	    throw new Error();

	// SELECT phase
	while (! webStack.empty()) {
	    SimpleJITWebTemporaryOperand w =
		(SimpleJITWebTemporaryOperand)webStack.pop();
	    igraph.add(w);
	    for(Iterator it = igraph.copy().iterator();
		it.hasNext(); ) {
		SimpleJITWebTemporaryOperand wi =
		    (SimpleJITWebTemporaryOperand)it.next();
		wi.copyInterferersToScatchForSubset(igraph);
	    }
	    SimpleJITIdentitySet scratchInterferers =
		w.getScratchInterferers();

	    SimpleJITIdentitySet usedVRegs = 
		new SimpleJITIdentitySet();
	    for(Iterator it = scratchInterferers.iterator();
		it.hasNext(); ) {
		SimpleJITWebTemporaryOperand wi =
		    (SimpleJITWebTemporaryOperand)it.next();
		SimpleJITVirtualRegisterOperand vr = 
		    wi.getScratchVirtualRegister();
		if (vr != null)
		    usedVRegs.add(vr);
	    }
	    SimpleJITIdentitySet vregSet = 
		new SimpleJITIdentitySet(vregs);
	    vregSet.diff(usedVRegs);
	    if (vregSet.size() > 0) { // found a color
		// choose the highest ranking vreg
		SimpleJITVirtualRegisterOperand vreg = 
		    (SimpleJITVirtualRegisterOperand)vregSet.remove();
		for(Iterator it = vregSet.iterator();
		    it.hasNext(); ) {
		    SimpleJITVirtualRegisterOperand vr =
			(SimpleJITVirtualRegisterOperand)it.next();
		    if (vr.getIndex() < vreg.getIndex())
			vreg = vr;
		}

		w.setScratchVirtualRegister(vreg);

		// Put the mapping from the web to the register into
		// the hash map
		SimpleJITWebTemporaryOperand.Set vreg_wset =
		    (SimpleJITWebTemporaryOperand.Set)
		    vreg2WebSet.get(vreg);
		if (vreg_wset == null) {
		    vreg_wset = 
			new SimpleJITWebTemporaryOperand.Set
			(w.getTypeCode());
		    vreg2WebSet.put(vreg, vreg_wset);
		}
		vreg_wset.add(w);
	    }
	    // keep going even if spill heuristic fails
	}

	// Assign vregs to webs and set mergedWebSetSet
	for(int i = 0; i < vregs.length; i++) {
	    SimpleJITWebTemporaryOperand.Set webset =
		(SimpleJITWebTemporaryOperand.Set)
		vreg2WebSet.get(vregs[i]);
	    if (webset != null) {
		for(Iterator it = webset.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w =
			(SimpleJITWebTemporaryOperand)
			it.next();
		    w.setVirtualRegister(vregs[i]);
		    igraph.remove(w); // remove the web from igraph
		}
		typeCode2MergedWebSetSet(typeCode)
		    .add(webset);
	    }
	}
    }





    private void sortWebSets(SimpleJITWebTemporaryOperand.Set[] webSets) {
	sortWebSets(webSets, 0, webSets.length - 1);
    }

    // Quick sort
    private void sortWebSets(SimpleJITWebTemporaryOperand.Set[] webSets, 
			     int low0,
			     int high0) {
	int low = low0;
	int high = high0;
	if (low >= high)
	    return;
	else if (low == high - 1) {
	    if (webSets[low].getWeightedRefCount()
		> webSets[high].getWeightedRefCount()) {
		SimpleJITWebTemporaryOperand.Set w = webSets[low];
		webSets[low] = webSets[high];
		webSets[high] = w;
	    }
	    return;
	}
	SimpleJITWebTemporaryOperand.Set pivot = 
	    webSets[(low + high) / 2];
	webSets[(low + high) / 2] = webSets[high];
	webSets[high] = pivot;
	while (low < high) {
	    while (webSets[low].getWeightedRefCount() <= 
		   pivot.getWeightedRefCount()
		   && low < high) {
		low++;
	    }
	    while (pivot.getWeightedRefCount() <= 
		   webSets[high].getWeightedRefCount()
		   && low < high) {
		high--;
	    }
	    if (low < high) {
		SimpleJITWebTemporaryOperand.Set w = webSets[low];
		webSets[low] = webSets[high];
		webSets[high] = w;
	    }
	}

	webSets[high0] = webSets[high];
	webSets[high] = pivot;
	sortWebSets(webSets, low0, low - 1);
	sortWebSets(webSets, high + 1, high0);
    }



    private void sortWebs(SimpleJITWebTemporaryOperand[] webs) {
	sortWebs(webs, 0, webs.length - 1);
    }

    // Quick sort
    private void sortWebs(SimpleJITWebTemporaryOperand[] webs, 
			  int low0,
			  int high0) {
	int low = low0;
	int high = high0;
	if (low >= high)
	    return;
	else if (low == high - 1) {
	    if (webs[low].getWeightedRefCount()
		> webs[high].getWeightedRefCount()) {
		SimpleJITWebTemporaryOperand w = webs[low];
		webs[low] = webs[high];
		webs[high] = w;
	    }
	    return;
	}
	SimpleJITWebTemporaryOperand pivot = webs[(low + high) / 2];
	webs[(low + high) / 2] = webs[high];
	webs[high] = pivot;
	while (low < high) {
	    while (webs[low].getWeightedRefCount() <= 
		   pivot.getWeightedRefCount()
		   && low < high) {
		low++;
	    }
	    while (pivot.getWeightedRefCount() <= 
		   webs[high].getWeightedRefCount()
		   && low < high) {
		high--;
	    }
	    if (low < high) {
		SimpleJITWebTemporaryOperand w = webs[low];
		webs[low] = webs[high];
		webs[high] = w;
	    }
	}

	webs[high0] = webs[high];
	webs[high] = pivot;
	sortWebs(webs, low0, low - 1);
	sortWebs(webs, high + 1, high0);
    }

    /**
     * Compute web interferences.
     */
    private void computeWebInterference() {
	SimpleJITIdentitySet workset = new SimpleJITIdentitySet();
	workset.addAll(blockSet.getBlockList().toArray());
	while (! workset.empty()) {
	    SimpleJITBasicBlock b = 
		(SimpleJITBasicBlock)workset.remove();
	    SimpleJITInstruction[] insts = b.getInstructions();

	    for(int i = 0; i < insts.length; i++) {
		SimpleJITInstruction inst = insts[i];
		SimpleJITOperand[] defs = null;
		SimpleJITTemporaryUseOperand[] liveOutUses = null;
		if (inst.isDefiner()) {
		    defs = inst.asDefiner().getDefOperands();
		}
		if (defs == null)
		    continue;
		// interferences between defs
		for(int j = 0; j < defs.length - 1; j++) {
		    if (! defs[j].isTemporaryDefOperand()
			|| !defs[j + 1].isTemporaryDefOperand())
			continue;
		    SimpleJITTemporaryDefOperand d1 = 
			defs[j].asTemporaryDefOperand();
		    SimpleJITTemporaryDefOperand d2 = 
			defs[j + 1].asTemporaryDefOperand();
		    if (d1.getUseSize() == 0
			|| d2.getUseSize() == 0) // dead def
			continue;
		    SimpleJITWebTemporaryOperand w1 = d1.getWeb();
		    SimpleJITWebTemporaryOperand w2 = d2.getWeb();
		    w1.addInterferer(w2);
		    w2.addInterferer(w1);
		}
		liveOutUses = inst.getLiveOutUsesAsArray();
		// interferences between a def and liveOutUses
		for(int j = 0; j < defs.length; j++) {
		    if (! defs[j].isTemporaryDefOperand())
			continue;
		    SimpleJITTemporaryDefOperand d =
			defs[j].asTemporaryDefOperand();
		    if (d.getUseSize() == 0) // dead def
			continue;
		    SimpleJITWebTemporaryOperand dw = d.getWeb();
		    for(int k = 0; k < liveOutUses.length; k++) {
			SimpleJITTemporaryUseOperand u = liveOutUses[k];
			SimpleJITWebTemporaryOperand uw = u.getWeb();
			dw.addInterferer(uw);
			uw.addInterferer(dw);
		    }
		}
	    }
	}
    }

    /**
     * Compute webs based on the def-use chains.
     */
    private void computeWebs() {
	SimpleJITIdentitySet workset = new SimpleJITIdentitySet();
	workset.addAll(blockSet.getBlockList().toArray());
	while (! workset.empty()) {
	    SimpleJITBasicBlock b = 
		(SimpleJITBasicBlock)workset.remove();
	    SimpleJITInstruction[] insts = b.getInstructions();

	    for(int i = 0; i < insts.length; i++) {
		SimpleJITInstruction inst = insts[i];
		if (inst.isUser()) {
		    SimpleJITOperand[] uses = 
			inst.asUser().getUseOperands();
		    for(int j = 0; j < uses.length; j++) {
			if (! (uses[j] 
			       instanceof SimpleJITTemporaryUseOperand))
			    continue;
			SimpleJITTemporaryUseOperand u = 
			    (SimpleJITTemporaryUseOperand)uses[j];
			if (u.getWeb() != null)
			    continue;
			SimpleJITWebTemporaryOperand web =
			    new SimpleJITWebTemporaryOperand
			    (u.getTemporary().getTypeCode());
			webSet.add(web);
			SimpleJITIdentitySet closure =
			    computeDefUseClosure(u);
			web.setDefUses(closure);
			int weightedRefCount = 0;
			for(Iterator it = closure.iterator();
			    it.hasNext(); ) {
			    SimpleJITTemporaryWrapperOperand w =
				(SimpleJITTemporaryWrapperOperand)
				it.next();
			    weightedRefCount += 
				1 * power(10, w.getLoopDepth());
			    if (w.getWeb() != null)
				throw new Error();
			    w.setWeb(web);
			}
			web.setRefCount(closure.size());
			web.setWeightedRefCount(weightedRefCount);
		    }
		}
		if (inst.isDefiner()) {
		    SimpleJITOperand[] defs = 
			inst.asDefiner().getDefOperands();
		    for(int j = 0; j < defs.length; j++) {
			if (! (defs[j] 
			       instanceof SimpleJITTemporaryDefOperand))
			    continue;
			SimpleJITTemporaryDefOperand d = 
			    (SimpleJITTemporaryDefOperand)defs[j];
			if (d.getWeb() != null)
			    continue;
			SimpleJITWebTemporaryOperand web =
			    new SimpleJITWebTemporaryOperand
			    (d.getTemporary().getTypeCode());
			webSet.add(web);
			SimpleJITIdentitySet closure =
			    computeDefUseClosure(d);
			web.setDefUses(closure);
			int weightedRefCount = 0;
			for(Iterator it = closure.iterator();
			    it.hasNext(); ) {
			    SimpleJITTemporaryWrapperOperand w =
				(SimpleJITTemporaryWrapperOperand)
				it.next();
			    weightedRefCount += 
				1 * power(10, w.getLoopDepth());
			    if (w.getWeb() != null)
				throw new Error();
			    w.setWeb(web);
			}
			web.setRefCount(closure.size());
			web.setWeightedRefCount(weightedRefCount);
		    }
		}
	    }
	}
	sjMethod.setNumberOfWebs(webSet.size());
    }

    private static int power(int a, int b) {
	int p = 1;
	for(int i = 0; i < b; i++) {
	    p *= a;
	}
	return p;
    }

    /**
     * A helper for computeWebs. Computes a transitive closure of
     * temporary wrapper operands via the def-use links.
     */
    private SimpleJITIdentitySet computeDefUseClosure
	(SimpleJITTemporaryWrapperOperand w) {
	SimpleJITIdentitySet closure = new SimpleJITIdentitySet();
	SimpleJITIdentitySet workset = new SimpleJITIdentitySet();
	workset.add(w);
	closure.add(w);
	while (! workset.empty()) {
	    SimpleJITTemporaryWrapperOperand du =
		(SimpleJITTemporaryWrapperOperand)workset.remove();
	    if (du.isTemporaryDefOperand()) {
		SimpleJITTemporaryDefOperand d = 
		    du.asTemporaryDefOperand();
		SimpleJITTemporaryUseOperand[] uses = d.getUses();
		for(int i = 0; i < uses.length; i++)
		    if (! closure.contains(uses[i])) {
			workset.add(uses[i]);
			closure.add(uses[i]);
		    }
	    } else if (du.isTemporaryUseOperand()) {
		SimpleJITTemporaryUseOperand u = 
		    du.asTemporaryUseOperand();
		SimpleJITTemporaryDefOperand[] defs = u.getDefs();
		for(int i = 0; i < defs.length; i++)
		    if (! closure.contains(defs[i])) {
			workset.add(defs[i]);
			closure.add(defs[i]);
		    }
	    }
	}
	return closure;
    }


    private SimpleJITVirtualRegisterOperand 
	du2vreg(SimpleJITTemporaryWrapperOperand du) {
	if (du == null)
	    throw new Error();
	SimpleJITWebTemporaryOperand w = du.getWeb();
	if (w == null)
	    throw new Error();
	SimpleJITVirtualRegisterOperand reg =
	    w.getVirtualRegister();
	if (reg == null)
	    throw new Error();
	return reg;
    }

    /**
     * After the register allocation is done, replace the webs in the
     * code (including reachingDef and liveUses sets) with virtual
     * registers.
     */
    private void replaceDefUseWithVirtualRegister() {
	SimpleJITBasicBlock.List blist =
	    blockSet.getBlockList().copy();
	while (! blist.empty()) {
	    SimpleJITBasicBlock b = blist.remove();
	    replaceDefUseWithVirtualRegisterOfBlock(b);
	    SimpleJITInstruction[] insts = b.getInstructions();
	    for(int i = 0; i < insts.length; i++) {
		SimpleJITInstruction inst = insts[i];
		replaceDefUseWithVirtualRegisterOfInstruction(inst);
		if (inst.isDefiner()) {
		    SimpleJITInstruction.Definer definer = 
			inst.asDefiner();
		    SimpleJITOperand[] defs = 
			definer.getDefOperands();
		    for(int j = 0; j < defs.length; j++) {
			SimpleJITOperand op = defs[j];
			if (op instanceof SimpleJITTemporaryWrapperOperand) {
			    SimpleJITTemporaryWrapperOperand du =
				(SimpleJITTemporaryWrapperOperand)defs[j];
			    defs[j] = du2vreg(du);
			}
		    }
		    definer.setDefOperands(defs);
		}
		if (inst.isUser()) {
		    SimpleJITInstruction.User user = 
			inst.asUser();
		    SimpleJITOperand[] uses = 
			user.getUseOperands();
		    for(int j = 0; j < uses.length; j++) {
			SimpleJITOperand op = uses[j];
			if (op instanceof SimpleJITTemporaryWrapperOperand) {
			    SimpleJITTemporaryWrapperOperand du =
				(SimpleJITTemporaryWrapperOperand)uses[j];
			    uses[j] = du2vreg(du);
			}
		    }
		    user.setUseOperands(uses);
		}
	    }
	}
    }

    /**
     * A helper for replaceDefUseWithVirtualRegister
     */
    private void replaceDefUseWithVirtualRegisterOfBlock
	(SimpleJITBasicBlock b) {
	SimpleJITIdentitySet reachingInDefs =
	    b.getReachingInDefs();
	SimpleJITIdentitySet reachingOutDefs =
	    b.getReachingOutDefs();
	SimpleJITIdentitySet liveInUses =
	    b.getLiveInUses();
	SimpleJITIdentitySet liveOutUses =
	    b.getLiveOutUses();
	for(Iterator it = reachingInDefs.copy().iterator();
	    it.hasNext(); ) {
	    SimpleJITTemporaryWrapperOperand du =
		(SimpleJITTemporaryWrapperOperand)it.next();
	    reachingInDefs.remove(du);
	    reachingInDefs.add(du2vreg(du));
	}
	for(Iterator it = reachingOutDefs.copy().iterator();
	    it.hasNext(); ) {
	    SimpleJITTemporaryWrapperOperand du =
		(SimpleJITTemporaryWrapperOperand)it.next();
	    reachingOutDefs.remove(du);
	    reachingOutDefs.add(du2vreg(du));
	}
	for(Iterator it = liveInUses.copy().iterator();
	    it.hasNext(); ) {
	    SimpleJITTemporaryWrapperOperand du =
		(SimpleJITTemporaryWrapperOperand)it.next();
	    liveInUses.remove(du);
	    liveInUses.add(du2vreg(du));
	}
	for(Iterator it = liveOutUses.copy().iterator();
	    it.hasNext(); ) {
	    SimpleJITTemporaryWrapperOperand du =
		(SimpleJITTemporaryWrapperOperand)it.next();
	    liveOutUses.remove(du);
	    liveOutUses.add(du2vreg(du));
	}
	b.setReachingInDefs(reachingInDefs);
	b.setReachingOutDefs(reachingOutDefs);
	b.setLiveInUses(liveInUses);
	b.setLiveOutUses(liveOutUses);
    }


    /**
     * A helper for replaceDefUseWithVirtualRegister
     */
    private void replaceDefUseWithVirtualRegisterOfInstruction
	(SimpleJITInstruction inst) {
	SimpleJITIdentitySet reachingInDefs =
	    inst.getReachingInDefs();
	SimpleJITIdentitySet reachingOutDefs =
	    inst.getReachingOutDefs();
	SimpleJITIdentitySet liveInUses =
	    inst.getLiveInUses();
	SimpleJITIdentitySet liveOutUses =
	    inst.getLiveOutUses();
	for(Iterator it = reachingInDefs.copy().iterator();
	    it.hasNext(); ) {
	    SimpleJITTemporaryWrapperOperand du =
		(SimpleJITTemporaryWrapperOperand)it.next();
	    reachingInDefs.remove(du);
	    reachingInDefs.add(du2vreg(du));
	}
	for(Iterator it = reachingOutDefs.copy().iterator();
	    it.hasNext(); ) {
	    SimpleJITTemporaryWrapperOperand du =
		(SimpleJITTemporaryWrapperOperand)it.next();
	    reachingOutDefs.remove(du);
	    reachingOutDefs.add(du2vreg(du));
	}
	for(Iterator it = liveInUses.copy().iterator();
	    it.hasNext(); ) {
	    SimpleJITTemporaryWrapperOperand du =
		(SimpleJITTemporaryWrapperOperand)it.next();
	    liveInUses.remove(du);
	    liveInUses.add(du2vreg(du));
	}
	for(Iterator it = liveOutUses.copy().iterator();
	    it.hasNext(); ) {
	    SimpleJITTemporaryWrapperOperand du =
		(SimpleJITTemporaryWrapperOperand)it.next();
	    liveOutUses.remove(du);
	    liveOutUses.add(du2vreg(du));
	}
	inst.setReachingInDefs(reachingInDefs);
	inst.setReachingOutDefs(reachingOutDefs);
	inst.setLiveInUses(liveInUses);
	inst.setLiveOutUses(liveOutUses);
    }



    /**
     * The driver for no-spill graph coloring register allocation
     * based on optimistic coloring.
     */
    private void allocateVirtualRegistersWithGreedyExactGraphColoring() {
	allocateVirtualRegistersWithGreedyExactGraphColoringFor(TypeCodes.INT);
	allocateVirtualRegistersWithGreedyExactGraphColoringFor(TypeCodes.REFERENCE);
	allocateVirtualRegistersWithGreedyExactGraphColoringFor(TypeCodes.LONG);
	allocateVirtualRegistersWithGreedyExactGraphColoringFor(TypeCodes.FLOAT);
	allocateVirtualRegistersWithGreedyExactGraphColoringFor(TypeCodes.DOUBLE);
	allocateVirtualRegistersWithGreedyExactGraphColoringFor(TypeCodes.RETURNADDRESS);
    }
    private void allocateVirtualRegistersWithGreedyExactGraphColoringFor
	(char typeCode) {
	boolean success = false;
	boolean success_ever = false;
	int nweb = 0;
	for(Iterator it = webSet.iterator();
	    it.hasNext(); ) {
	    SimpleJITWebTemporaryOperand w =
		(SimpleJITWebTemporaryOperand)it.next();
	    if (w.getTypeCode() == typeCode)
		nweb++;
	}

	nweb++; // for a complete interference graph, this is needed...

	//d("nweb = " + nweb);
	if (nweb < 4) { // linear search
	    for(int i = nweb; i >= 0; i--) {
		success =
		    allocateVirtualRegistersWithGreedyExactGraphColoringFor
		    (typeCode, i);
		success_ever |= success;
		if (! success)
		    break;
	    }
	} else { // binary search
	    int high = nweb;
	    int low  = 0;
	    while (high - low > 1) {
		//d("high = " + high + ", low = " + low);
		int medium = (high + low) / 2;
		//d("trying medium = " + medium);
		success = 
		    allocateVirtualRegistersWithGreedyExactGraphColoringFor
		    (typeCode, medium);
		success_ever |= success;
		//d("success = " + success);
		if (success)
		    high = medium;
		else
		    low = medium;
	    }
	}

	if (! success_ever)
	    throw new Error("Register allocation failed");

	sortAndRenameMergedWebSetSet(typeCode);
    }

    /**
     * Apply no-spill graph coloring register allocation based on
     * optimistic coloring with K virtual registers of the given type.
     * @param typeCode the type
     * @param K the number of given virtual registers
     * @return true if the allocation performed without spills, false
     * otherwise.
     */
    private boolean allocateVirtualRegistersWithGreedyExactGraphColoringFor
	(char typeCode,
	 int K) {
	SimpleJITStack webStack = new SimpleJITStack();

	// Prepare virtual registers
	SimpleJITVirtualRegisterOperand[] vregs =
	    new SimpleJITVirtualRegisterOperand[K];
	// The map from a virtual reg to the web set
	IdentityHashMap vreg2WebSet = new IdentityHashMap();
	for(int i = 0; i < vregs.length; i++) {
	    vregs[i] = vrf.create(typeCode, i);
	}
	
	SimpleJITIdentitySet igraph = webSet.copy();

	// Initialize scratch interference lists and reset virtual
	// registers in webs.
	for(Iterator it = igraph.iterator();
	    it.hasNext(); ) { 
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    // Prune webs of wrong types
	    if (w.getTypeCode() != typeCode) {
		it.remove();
	    } else {
		w.copyInterferersToScatch();
		w.setScratchVirtualRegister(null);
	    }
	}

	// SIMPLIFY and SPILL phases
	while (! igraph.empty()) {
	    SimpleJITWebTemporaryOperand simplifiable = 
		//findSimplifiableWebGreedily(igraph, vregs.length);
		findSimplifiableWebHumbly(igraph, vregs.length);
	    if (simplifiable != null) {
		// SIMPLIFY
		simplifiable.setPotentialSpillFlag(false);
		webStack.push(simplifiable);
		igraph.remove(simplifiable);
		// Remove interference edges
		for(Iterator it = igraph.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w = 
			(SimpleJITWebTemporaryOperand)it.next();
		    w.removeScatchInterferer(simplifiable);
		}
	    } else {
		// SPILL
 		// search for the smallest spillcost/degree
		SimpleJITWebTemporaryOperand potentialSpill = null;
 		for(Iterator it = igraph.iterator();
 		    it.hasNext(); ) {
 		    SimpleJITWebTemporaryOperand w =
 			(SimpleJITWebTemporaryOperand)it.next();
		    if (potentialSpill == null) {
			potentialSpill = w;
 		    } else if ((w.getWeightedRefCount() 
				* potentialSpill.getScratchInterferers().size())
			       < (potentialSpill.getWeightedRefCount()
				  * w.getScratchInterferers().size())) {
 			potentialSpill = w;
 		    }
 		}
		igraph.remove(potentialSpill);
		if (potentialSpill == null
		    || potentialSpill.getScratchInterferers()
		    .size() < vregs.length) {
		    throw new Error();
		}
		potentialSpill.setPotentialSpillFlag(true);
		webStack.push(potentialSpill);
		igraph.remove(potentialSpill);
		// Remove interference edges
		for(Iterator it = igraph.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w = 
			(SimpleJITWebTemporaryOperand)it.next();
		    w.removeScatchInterferer(potentialSpill);
		}
	    }
	}

	// igraph must be empty here

	if (! igraph.empty())
	    throw new Error();

	// SELECT phase
	while (! webStack.empty()) {
	    SimpleJITWebTemporaryOperand w =
		(SimpleJITWebTemporaryOperand)webStack.pop();
	    igraph.add(w);
	    for(Iterator it = igraph.copy().iterator();
		// Copy igraph. Don't run more than one iterator on
		// the same set
		it.hasNext(); ) {
		SimpleJITWebTemporaryOperand wi =
		    (SimpleJITWebTemporaryOperand)it.next();
		wi.copyInterferersToScatchForSubset(igraph);
	    }
	    SimpleJITIdentitySet scratchInterferers =
		w.getScratchInterferers();

	    SimpleJITIdentitySet usedVRegs = 
		new SimpleJITIdentitySet();
	    for(Iterator it = scratchInterferers.iterator();
		it.hasNext(); ) {
		SimpleJITWebTemporaryOperand wi =
		    (SimpleJITWebTemporaryOperand)it.next();
		SimpleJITVirtualRegisterOperand vr = 
		    wi.getScratchVirtualRegister();
		if (vr != null)
		    usedVRegs.add(vr);
	    }
	    SimpleJITIdentitySet vregSet = 
		new SimpleJITIdentitySet(vregs);
	    vregSet.diff(usedVRegs);
	    if (vregSet.size() > 0) { // found a color

		// choose the highest ranking vreg
		SimpleJITVirtualRegisterOperand vreg =
		    (SimpleJITVirtualRegisterOperand)vregSet.remove();
		for(Iterator it = vregSet.iterator();
		    it.hasNext(); ) {
		    SimpleJITVirtualRegisterOperand vr =
			(SimpleJITVirtualRegisterOperand)it.next();
		    if (vr.getIndex() < vreg.getIndex())
			vreg = vr;
		}

		w.setScratchVirtualRegister(vreg);

		// Put the mapping from the web to the register into
		// the hash map
		SimpleJITWebTemporaryOperand.Set vreg_wset =
		    (SimpleJITWebTemporaryOperand.Set)
		    vreg2WebSet.get(vreg);
		if (vreg_wset == null) {
		    vreg_wset = 
			new SimpleJITWebTemporaryOperand.Set
			(w.getTypeCode());
		    vreg2WebSet.put(vreg, vreg_wset);
		}
		vreg_wset.add(w);
	    } else { // actual spill
		return false;
	    }
	}

	// Allocation succeeded

	// Assign vregs to webs and set mergedWebSetSet
	resetMergedWebSetSetOnType(typeCode);
	for(int i = 0; i < vregs.length; i++) {
	    SimpleJITWebTemporaryOperand.Set webset =
		(SimpleJITWebTemporaryOperand.Set)
		vreg2WebSet.get(vregs[i]);
	    if (webset != null) {
		for(Iterator it = webset.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w =
			(SimpleJITWebTemporaryOperand)
			it.next();
		    w.setVirtualRegister(vregs[i]);
		}
		typeCode2MergedWebSetSet(typeCode)
		    .add(webset);
	    }
	}

	return true;
    }



    /**
     * Register allocation based on weighted reference counts and
     * a simple linear greedy packing.
     */
    private void allocateVirtualRegistersWithLinearPacking() {
	SimpleJITWebTemporaryOperand[] webs =
	    new SimpleJITWebTemporaryOperand[webSet.size()];
	webSet.toArray(webs);
	sortWebs(webs);
	HashMap webReplaceMap = new HashMap();

	resetMergedWebSetSetOnType(TypeCodes.INT);
	resetMergedWebSetSetOnType(TypeCodes.REFERENCE);
	resetMergedWebSetSetOnType(TypeCodes.LONG);
	resetMergedWebSetSetOnType(TypeCodes.FLOAT);
	resetMergedWebSetSetOnType(TypeCodes.DOUBLE);
	resetMergedWebSetSetOnType(TypeCodes.RETURNADDRESS);

	// Pack (contiguous) O(n)
	{
	    int i = webs.length - 1;
	    while (i >= 0) {
		SimpleJITWebTemporaryOperand wi = webs[i];
		SimpleJITWebTemporaryOperand.Set mwebSet = 
		    new SimpleJITWebTemporaryOperand.Set(wi.getTypeCode());
		mwebSet.add(wi);

		int j = i - 1;
		while (j >= 0) {
		    SimpleJITWebTemporaryOperand wj = webs[j];
		    if (mwebSet.getTypeCode() == wj.getTypeCode()
			&& ! mwebSet.getInterferers().contains(wj)) {
			mwebSet.add(wj);
		    } else {
			break;
		    }
		    j--;
		}
		typeCode2MergedWebSetSet(wi.getTypeCode()).add(mwebSet);
		i = j;
	    }
	}

	allocateVirtualRegistersWithLinearPacking_helper(TypeCodes.INT);
	allocateVirtualRegistersWithLinearPacking_helper(TypeCodes.FLOAT);
	allocateVirtualRegistersWithLinearPacking_helper(TypeCodes.LONG);
	allocateVirtualRegistersWithLinearPacking_helper(TypeCodes.DOUBLE);
	allocateVirtualRegistersWithLinearPacking_helper(TypeCodes.REFERENCE);
	allocateVirtualRegistersWithLinearPacking_helper(TypeCodes.RETURNADDRESS);
    }

    private void allocateVirtualRegistersWithLinearPacking_helper
	(char typeCode) {
	SimpleJITIdentitySet mergedWebSetSet = 
	    typeCode2MergedWebSetSet(typeCode);
	// Allocate virtual registers
	SimpleJITWebTemporaryOperand.Set[] mergedWebSets =
	    new SimpleJITWebTemporaryOperand.Set
	    [mergedWebSetSet.size()];
	mergedWebSetSet.toArray(mergedWebSets);

	sortWebSets(mergedWebSets);

	int regCounter = 0;
	for(int i = mergedWebSets.length - 1; i >= 0; i--) {
	    SimpleJITWebTemporaryOperand.Set webset = mergedWebSets[i];
	    if (webset.getTypeCode() != typeCode)
		throw new Error();
	    SimpleJITVirtualRegisterOperand reg = 
		vrf.create(webset.getTypeCode(), regCounter);
	    webset.setVirtualRegister(reg);
	    regCounter++;
	}

    }

    /**
     * The driver for normal graph coloring register allocation
     * based on optimistic coloring.
     */
    private void allocateVirtualRegistersWithNormalGraphColoring() {
	allocateVirtualRegistersWithNormalGraphColoringFor(TypeCodes.INT);
	allocateVirtualRegistersWithNormalGraphColoringFor(TypeCodes.REFERENCE);
	allocateVirtualRegistersWithNormalGraphColoringFor(TypeCodes.LONG);
	allocateVirtualRegistersWithNormalGraphColoringFor(TypeCodes.FLOAT);
	allocateVirtualRegistersWithNormalGraphColoringFor(TypeCodes.DOUBLE);
	allocateVirtualRegistersWithNormalGraphColoringFor(TypeCodes.RETURNADDRESS);
    }
    private void allocateVirtualRegistersWithNormalGraphColoringFor
	(char typeCode) {
	SimpleJITIdentitySet igraph = webSet.copy();

	int nweb = 0;
	for(Iterator it = webSet.iterator();
	    it.hasNext(); ) {
	    SimpleJITWebTemporaryOperand w =
		(SimpleJITWebTemporaryOperand)it.next();
	    if (w.getTypeCode() == typeCode)
		nweb++;
	}
//	nweb++; // for a complete interference graph, this is needed...

	resetNormalGCNReg2TotalSpillCost(typeCode, nweb);

	for(Iterator it = igraph.iterator();
	    it.hasNext(); ) { 
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    if (w.getTypeCode() != typeCode) {
		it.remove();
	    }
	}

	for(int i = 1; i < nweb + 1; i++) {
	    allocateVirtualRegistersWithNormalGraphColoringFor
		(typeCode, 0, i, igraph.copy());
	    int[] nreg2cost = getNormalGCNReg2TotalSpillCost(typeCode);
	    int currCost = nreg2cost[i - 1];
	    if (currCost == 0)
		break; // all webs were allocated
	}
    }

    /**
     * Apply normal graph coloring register allocation based on
     * optimistic coloring with nreg virtual register of the given
     * type.  Try allocate nreg register to the most important web,
     * but will leave other webs.
     * @param typeCode the type
     * @param start_reg the virtual register to start allocation with
     * @param nreg the number of the virtual register starting at start_reg
     */
    private void allocateVirtualRegistersWithNormalGraphColoringFor
	(char typeCode,
	 int start_reg,
	 int nreg,
	 SimpleJITIdentitySet igraph) {
	SimpleJITStack webStack = new SimpleJITStack();

	SimpleJITIdentitySet spilledWebs = new SimpleJITIdentitySet();

	// Prepare virtual registers
	SimpleJITVirtualRegisterOperand[] vregs =
	    new SimpleJITVirtualRegisterOperand[nreg];
	// The map from a virtual reg to the web set
	IdentityHashMap vreg2WebSet = new IdentityHashMap();
	for(int i = 0; i < nreg; i++) {
	    vregs[i] = vrf.create(typeCode, start_reg + i);
	}
	
	// Initialize scratch interference lists and webs' virtual
	// register.
	for(Iterator it = igraph.copy().iterator();
	    it.hasNext(); ) { 
	    SimpleJITWebTemporaryOperand w = 
		(SimpleJITWebTemporaryOperand)it.next();
	    if (w.getTypeCode() != typeCode) {
		throw new Error();
	    } else {
		w.copyInterferersToScatchForSubset(igraph);
		w.setScratchVirtualRegister(null);
	    }
	}

	// SIMPLIFY and SPILL phases
	while (! igraph.empty()) {
	    SimpleJITWebTemporaryOperand simplifiable = 
		findSimplifiableWeb(igraph, vregs.length);
	    //findSimplifiableWebGreedily(igraph, vregs.length);
	    if (simplifiable != null) {
		// SIMPLIFY
		simplifiable.setPotentialSpillFlag(false);
		webStack.push(simplifiable);
		igraph.remove(simplifiable);
		// Remove interference edges
		for(Iterator it = igraph.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w = 
			(SimpleJITWebTemporaryOperand)it.next();
		    w.removeScatchInterferer(simplifiable);
		}
	    } else {
		// SPILL
 		// search for the smallest spillcost/degree
		SimpleJITWebTemporaryOperand potentialSpill = null;
 		for(Iterator it = igraph.iterator();
 		    it.hasNext(); ) {
 		    SimpleJITWebTemporaryOperand w =
 			(SimpleJITWebTemporaryOperand)it.next();
		    if (potentialSpill == null) {
			potentialSpill = w;
 		    } else if ((w.getWeightedRefCount() 
				* potentialSpill.getScratchInterferers().size())
			       < (potentialSpill.getWeightedRefCount()
				  * w.getScratchInterferers().size())) {
 			potentialSpill = w;
 		    }
 		}
		igraph.remove(potentialSpill);

		if (potentialSpill == null
		    || potentialSpill.getScratchInterferers()
		    .size() < vregs.length) {
		    throw new Error();
		}
		potentialSpill.setPotentialSpillFlag(true);
		webStack.push(potentialSpill);
		igraph.remove(potentialSpill);
		// Remove interference edges
		for(Iterator it = igraph.iterator();
		    it.hasNext(); ) {
		    SimpleJITWebTemporaryOperand w = 
			(SimpleJITWebTemporaryOperand)it.next();
		    w.removeScatchInterferer(potentialSpill);
		}
	    }
	}

	// igraph must be empty here

	if (! igraph.empty())
	    throw new Error();

	// SELECT phase
	while (! webStack.empty()) {
	    SimpleJITWebTemporaryOperand w =
		(SimpleJITWebTemporaryOperand)webStack.pop();
	    igraph.add(w);
	    for(Iterator it = igraph.copy().iterator();
		it.hasNext(); ) {
		SimpleJITWebTemporaryOperand wi =
		    (SimpleJITWebTemporaryOperand)it.next();
		wi.copyInterferersToScatchForSubset(igraph);
	    }
	    SimpleJITIdentitySet scratchInterferers =
		w.getScratchInterferers();

	    SimpleJITIdentitySet usedVRegs = 
		new SimpleJITIdentitySet();
	    for(Iterator it = scratchInterferers.iterator();
		it.hasNext(); ) {
		SimpleJITWebTemporaryOperand wi =
		    (SimpleJITWebTemporaryOperand)it.next();
		SimpleJITVirtualRegisterOperand vr = 
		    wi.getScratchVirtualRegister();
		if (vr != null)
		    usedVRegs.add(vr);
	    }
	    SimpleJITIdentitySet vregSet = 
		new SimpleJITIdentitySet(vregs);
	    vregSet.diff(usedVRegs);
	    if (vregSet.size() > 0) { // found a color
		SimpleJITVirtualRegisterOperand vreg =
		    (SimpleJITVirtualRegisterOperand)vregSet.remove();

// 		// choose the highest ranking vreg
// 		for(Iterator it = vregSet.iterator();
// 		    it.hasNext(); ) {
// 		    SimpleJITVirtualRegisterOperand vr =
// 			(SimpleJITVirtualRegisterOperand)it.next();
// 		    if (vr.getIndex() < vreg.getIndex())
// 			vreg = vr;
// 		}

		w.setScratchVirtualRegister(vreg);
	    } else {
		spilledWebs.add(w);
		igraph.remove(w);
	    }
	    // keep going even if spill heuristic fails
	}

	// Assigned webs are in igraph
	// Spilled webs are in spilledWebs
	int totalSpillCost = 0;
	for(Iterator it = spilledWebs.iterator();
	    it.hasNext();) {
	    SimpleJITWebTemporaryOperand w =
		(SimpleJITWebTemporaryOperand)it.next();
	    totalSpillCost += w.getWeightedRefCount();
	}

	getNormalGCNReg2TotalSpillCost(typeCode)[nreg - 1] = totalSpillCost;
    }

    private void writeResultsForNormalGraphColoring() {
	String output = "";
	S3Method m = sjMethod.getS3Method();
	String mname = m.getSelector().toString();
	String tcname = m.getDeclaringType().getContext().toString();
	output += tcname + "\n" + mname + "\n";
	output = writeResultsForNormalGraphColoring_helper(TypeCodes.INT, output);
	output = writeResultsForNormalGraphColoring_helper(TypeCodes.REFERENCE, output);
	output = writeResultsForNormalGraphColoring_helper(TypeCodes.LONG, output);
	output = writeResultsForNormalGraphColoring_helper(TypeCodes.FLOAT, output);
	output = writeResultsForNormalGraphColoring_helper(TypeCodes.DOUBLE, output);
	output = writeResultsForNormalGraphColoring_helper(TypeCodes.RETURNADDRESS, output);
	output += "\n" + (sjMethod.optimizationTime) + "\n";
	resultWriter.println(output);
    }

    private String writeResultsForNormalGraphColoring_helper(char typeCode, String output) {
	output += typeCode + "\n";
	int[] normalGCNReg2TotalSpillCost = 
	    getNormalGCNReg2TotalSpillCost(typeCode);

	output += normalGCNReg2TotalSpillCost.length + "\n";

	for(int i = 0; i < normalGCNReg2TotalSpillCost.length; i++) {
	    output += "\t" + normalGCNReg2TotalSpillCost[i] + "\n";
	}
	return output;
    }

    private int[] normalGCNReg2TotalSpillCost_I;
    private int[] normalGCNReg2TotalSpillCost_A;
    private int[] normalGCNReg2TotalSpillCost_F;
    private int[] normalGCNReg2TotalSpillCost_D;
    private int[] normalGCNReg2TotalSpillCost_J;
    private int[] normalGCNReg2TotalSpillCost_U;

    private int[] getNormalGCNReg2TotalSpillCost(char typeCode) {
	switch(typeCode) {
	case TypeCodes.INT:
	    return normalGCNReg2TotalSpillCost_I;
	case TypeCodes.REFERENCE:
	    return normalGCNReg2TotalSpillCost_A;
	case TypeCodes.FLOAT:
	    return normalGCNReg2TotalSpillCost_F;
	case TypeCodes.LONG:
	    return normalGCNReg2TotalSpillCost_D;
	case TypeCodes.DOUBLE:
	    return normalGCNReg2TotalSpillCost_J;
	case TypeCodes.RETURNADDRESS:
	    return normalGCNReg2TotalSpillCost_U;
	default:
	    throw new Error("unexpected " + typeCode);
	}
    }
    private void resetNormalGCNReg2TotalSpillCost(char typeCode, int length) {
	switch(typeCode) {
	case TypeCodes.INT:
	    normalGCNReg2TotalSpillCost_I = new int[length];
	    break;
	case TypeCodes.REFERENCE:
	    normalGCNReg2TotalSpillCost_A = new int[length];
	    break;
	case TypeCodes.FLOAT:
	    normalGCNReg2TotalSpillCost_F = new int[length];
	    break;
	case TypeCodes.LONG:
	    normalGCNReg2TotalSpillCost_D = new int[length];
	    break;
	case TypeCodes.DOUBLE:
	    normalGCNReg2TotalSpillCost_J = new int[length];
	    break;
	case TypeCodes.RETURNADDRESS:
	    normalGCNReg2TotalSpillCost_U = new int[length];
	    break;
	default:
	    throw new Error();
	}
    }

    private void cutTailOfnormalGCNReg2TotalSpillCosts() {
	cutTailOfnormalGCNReg2TotalSpillCosts_helper(TypeCodes.INT);
	cutTailOfnormalGCNReg2TotalSpillCosts_helper(TypeCodes.LONG);
	cutTailOfnormalGCNReg2TotalSpillCosts_helper(TypeCodes.REFERENCE);
	cutTailOfnormalGCNReg2TotalSpillCosts_helper(TypeCodes.FLOAT);
	cutTailOfnormalGCNReg2TotalSpillCosts_helper(TypeCodes.DOUBLE);
	cutTailOfnormalGCNReg2TotalSpillCosts_helper(TypeCodes.RETURNADDRESS);
    }

    // Remove elements of value 0 at the end of the arrays, except one.
    private void cutTailOfnormalGCNReg2TotalSpillCosts_helper(char typeCode) {
	int[] normalGCNReg2TotalSpillCost = 
	    getNormalGCNReg2TotalSpillCost(typeCode);
	int realLength = normalGCNReg2TotalSpillCost.length;
	for(int i = normalGCNReg2TotalSpillCost.length - 1; i >= 0; i--) {
	    if (normalGCNReg2TotalSpillCost[i] == 0)
		realLength = i + 1;
	    else
		break;
	}
	resetNormalGCNReg2TotalSpillCost(typeCode, realLength);
	System.arraycopy(normalGCNReg2TotalSpillCost, 0,
			 getNormalGCNReg2TotalSpillCost(typeCode), 0,
			 realLength);
    }

}
