package s3.services.simplejit.ir;

import ovm.core.repository.Attribute;
import ovm.core.repository.ExceptionHandler;
import ovm.core.domain.Code;
import s3.core.domain.S3Code;
import s3.core.domain.S3ByteCode;
import s3.core.domain.S3Method;
import ovm.core.repository.TypeCodes;
import s3.services.simplejit.ir.SimpleJITOperand.SimpleJITVirtualRegisterOperand;
import s3.core.domain.S3Constants;
import ovm.util.ArrayList;
import s3.services.simplejit.util.SimpleJITIdentitySet;

public class SimpleJITIRCode extends S3Code {

    public static Code.Kind KIND = new Code.Kind() {};

    public long optimizationTime = 0L;

    private S3Method method;
    private S3Constants constantPool;
    private SimpleJITBasicBlock.Set blockSet;
    private int numberOfWebs;

    private boolean inliningFlag; // whether inlining occurred in this method
    private ArrayList inlinedMethods;

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

    public SimpleJITIRCode(S3Method method,
			   S3Constants cp,
			   SimpleJITBasicBlock.Set blockSet) {
	super(method);
	this.constantPool = cp;
	this.blockSet = blockSet;
    }

    public void bang(Code c) {
	throw new Error("Not implemented");
    }

    public Code.Kind getKind() {
	return KIND;
    }

    public S3Constants getConstantPool() {
	return constantPool;
    }
    public void setConstantPool(S3Constants cp) {
	constantPool = cp;
    }
    public byte[] getBytes() {
	throw new Error("Not implemented");
    }
    public Attribute[] getAttributes() {
	throw new Error("Not implemented");
    }
    public int getLineNumber(int pc) {
	throw new Error("Not implemented");
    }
    public ExceptionHandler[] getExceptionHandlers() {
	throw new Error("Not implemented");
    }

    public S3Method getS3Method() {
	return itsMethod;
    }

    public SimpleJITBasicBlock.Set getBlockSet() {
	return blockSet;
    }

    public SimpleJITVirtualRegisterOperand[] 
	getVirtualRegisterTable(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);
	}
    }
    public void setVirtualRegisterTables
	(SimpleJITVirtualRegisterOperand[] vtI,
	 SimpleJITVirtualRegisterOperand[] vtA,
	 SimpleJITVirtualRegisterOperand[] vtJ,
	 SimpleJITVirtualRegisterOperand[] vtF,
	 SimpleJITVirtualRegisterOperand[] vtD,
	 SimpleJITVirtualRegisterOperand[] vtU) {
	vregTable_I = vtI;
	vregTable_A = vtA;
	vregTable_J = vtJ;
	vregTable_F = vtF;
	vregTable_D = vtD;
	vregTable_U = vtU;
    }

    public void setVirtualRegisterTable
	(char typeCode,
	 SimpleJITVirtualRegisterOperand[] table) {
	switch(typeCode) {
	case TypeCodes.INT:
	    vregTable_I = table;
	    break;
	case TypeCodes.REFERENCE:
	    vregTable_A = table;
	    break;
	case TypeCodes.FLOAT:
	    vregTable_F = table;
	    break;
	case TypeCodes.LONG:
	    vregTable_J = table;
	    break;
	case TypeCodes.DOUBLE:
	    vregTable_D = table;
	    break;
	case TypeCodes.RETURNADDRESS:
	    vregTable_U = table;
	    break;
	default:
	    throw new Error("unexpected " + typeCode);
	}
    }

    public void setInliningFlag() {
	inliningFlag = true;
    }
    public boolean getInliningFlag() {
	return inliningFlag;
    }

    public void addInlinedMethod(SimpleJITIRCode m) {
	if (inlinedMethods == null)
	    inlinedMethods = new ArrayList();
	inlinedMethods.add(m);
    }

    public void adjustVirtualRegisterTable
	(SimpleJITVirtualRegisterOperand vreg,
	 int delta) {
	char typeCode = vreg.getTypeCode();
	SimpleJITVirtualRegisterOperand[] table =
	    getVirtualRegisterTable(typeCode);
	for(int i = 0; i < table.length; i++) {
	    if (vreg.equals(table[i])) {
		table[i].setWeightedRefCount
		    (table[i].getWeightedRefCount() + delta);
		return;
	    }
	}

	for(int i = 0; i < table.length; i++) {
	    System.err.println("table[" + i + "] = " + table[i]);
	}
	throw new Error(" vreg " + vreg + " not found");
    }

    public void setNumberOfWebs(int n) {
	numberOfWebs = n;
    }

    public int estimateIRCodeSize() {
	int codeSize = 0;

	int numberOfRegs = 
	    vregTable_I.length
	    + vregTable_A.length
	    + vregTable_J.length
	    + vregTable_F.length
	    + vregTable_D.length
	    + vregTable_U.length;

	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++) {
		if (insts[i] instanceof SimpleJITInstruction.Call)
		    codeSize += 4 + (numberOfRegs < 6 ? numberOfRegs : 6);
		else
		    codeSize += 4;
	    }
	}
	codeSize += 4 * numberOfRegs;
	codeSize += 6 * numberOfWebs;
	return codeSize;
    }

    public int getBytecodeSize() {
	S3ByteCode bytecode = (S3ByteCode)itsMethod.getCode(S3ByteCode.KIND);
	return bytecode.getBytes().length;
    }
}
