package s3.services.simplejit.bootimage;

import ovm.core.domain.Blueprint;
import ovm.core.domain.Method;
import ovm.core.execution.NativeConstants;
import ovm.core.repository.Selector;
import ovm.util.OVMError;
import s3.core.domain.S3NativeCode;
import s3.services.bootimage.Ephemeral;
import s3.services.simplejit.CodeGenerator;
import s3.services.simplejit.CompilerVMInterface;
import s3.services.simplejit.SimpleJIT;
import s3.util.PragmaTransformCallsiteIR;

/**
 * @author Christian Grothoff
 * @author Hiroshi Yamauchi
 **/
public class SimpleJITBootImageCompiler 
    implements Ephemeral.Void {

    private final boolean debugPrint;
    private final boolean opt;
    private final CompilerVMInterface compilerVMInterface;
    private SimpleJIT jit;

    SimpleJITBootImageCompiler
	(boolean debugPrint,
	 boolean opt,
	 CompilerVMInterface compilerVMInterface) {
        this.debugPrint = debugPrint;
        this.opt = opt;
        this.compilerVMInterface = compilerVMInterface;
        if ((NativeConstants.OVM_X86 && NativeConstants.OVM_PPC)
            || ((! NativeConstants.OVM_X86) && (! NativeConstants.OVM_PPC))) {
            throw new Error("OVM_X86 xor OVM_PPC != 1 in NativeConstants.java");
        } else if (NativeConstants.OVM_X86) {
            jit = new s3.services.simplejit.x86.SimpleJITImpl(
                    compilerVMInterface, debugPrint, opt);
        } else if (NativeConstants.OVM_PPC) {
            jit = new s3.services.simplejit.powerpc.SimpleJITImpl(
                    compilerVMInterface, debugPrint, opt);
        }
    }

    public CompilerVMInterface getCompilerVMInterface() {
        return compilerVMInterface;
    }

    CodeGenerator.Precomputed getCodeGeneratorPrecomputed() {
        return jit.getCodeGeneratorPrecomputed();
    }
    
    public String getStackLayoutAsCFunction() {
	return jit.getStackLayoutAsCFunction();
    }

    /**
     * Compile the given method, set the appropriate fields
     * in the Blueprint/VTBL.
     **/
    public void compile(Method meth, Blueprint bp) {
        Selector.Method sel = meth.getSelector();

	// If the method implements some pragma, skip it
	Blueprint pbp = meth.getMode().isStatic()
	    ? bp.getSharedState().getBlueprint()
	    : bp;
	if (PragmaTransformCallsiteIR
	    .descendantDeclaredBy(sel, pbp) != null) {
	    //d("Due to its pragma, skipping " + sel);
	    return; // do not compile it
	}

	//d("[JIT] Compiling " + sel);
        try {
            S3NativeCode cc = jit.compile(meth);
            if (cc != null) {
                //d("[JIT] " + meth + " @ " + Integer.toHexString(cc.getCodeEntry().asAbsoluteInt()));
                meth.addCode(cc);
            }
        } catch (OVMError.Internal ie) {
	    // fatal error: abort the whole compilation
	    ie.printStackTrace();
            throw new OVMError.Internal
		("[SimpleJIT] ERROR : " + ie + 
		 " : Could not compile " + sel);
        } catch (Throwable t) {
            System.err.println("[SimpleJIT] ERROR : " + t + 
	      " : Could not compile " + sel);
            t.printStackTrace();
        }
    }
}
