package s3.services.bootimage;

import java.io.IOException;
import java.util.HashMap;

import ovm.core.domain.Blueprint;
import ovm.core.domain.ConstantResolvedInstanceFieldref;
import ovm.core.domain.ConstantResolvedInstanceMethodref;
import ovm.core.domain.ConstantResolvedInterfaceMethodref;
import ovm.core.domain.ConstantResolvedStaticFieldref;
import ovm.core.domain.ConstantResolvedStaticMethodref;
import ovm.core.domain.DomainDirectory;
import ovm.core.domain.Field;
import ovm.core.domain.JavaUserDomain;
import ovm.core.domain.LinkageException;
import ovm.core.domain.MemberResolver;
import ovm.core.domain.Method;
import ovm.core.domain.Oop;
import ovm.core.domain.ResolutionInfo;
import ovm.core.domain.Type;
import ovm.core.execution.PragmaHasNativeSubstitute;
import ovm.core.repository.Attribute;
import ovm.core.repository.Descriptor;
import ovm.core.repository.JavaNames;
import ovm.core.repository.RepositoryClass;
import ovm.core.repository.RepositoryUtils;
import ovm.core.repository.Selector;
import ovm.core.repository.TypeName;
import ovm.core.repository.UnboundSelector;
import ovm.core.repository.Attribute.LocalVariableTable;
import ovm.core.services.format.JavaFormat;
import ovm.core.services.memory.MemoryManager;
import ovm.core.services.memory.PragmaNoBarriers;
import ovm.core.services.memory.VM_Address;
import ovm.core.stitcher.ThreadServiceConfigurator;
import ovm.core.stitcher.ThreadServicesFactory;
import ovm.services.bytecode.Instruction;
import ovm.services.bytecode.InstructionBuffer;
import ovm.services.bytecode.InstructionSet;
import ovm.services.bytecode.JVMConstants;
import ovm.services.bytecode.editor.CodeFragmentEditor;
import ovm.services.bytecode.editor.Cursor;
import ovm.services.bytecode.editor.InstructionEditVisitor;
import ovm.services.bytecode.editor.LinearPassController;
import ovm.services.bytecode.editor.LocalsShifter;
import ovm.services.bytecode.editor.Marker;
import ovm.services.bytecode.editor.CodeFragmentEditor.ExceptionHandlerList;
import ovm.services.bytecode.reader.ByteCodeConstants;
import ovm.services.bytecode.reader.ByteCodeConstants.Attributes;
import ovm.services.threads.UserLevelThreadManager;
import ovm.util.ArrayList;
import ovm.util.NumberRanges;
import ovm.util.PragmaUnsafe;
import ovm.util.logging.Logger;
import s3.core.domain.S3Blueprint;
import s3.core.domain.S3ByteCode;
import s3.core.domain.S3Constants;
import s3.core.domain.S3ConstantsBuilder;
import s3.core.domain.S3Domain;
import s3.core.domain.S3MemberResolver;
import s3.core.domain.S3Method;
import s3.core.domain.S3Type;
import s3.services.bootimage.BytecodeToOVMIR.Converter;
import s3.services.transactions.Optimizer;
import s3.services.transactions.S3Transaction;
import s3.services.transactions.Transaction;
import s3.util.PragmaAtomic;
import s3.util.PragmaException;
import s3.util.PragmaInline;
import s3.util.PragmaNoPollcheck;
import s3.util.PragmaTransformCallsiteIR;

/**
 * Rewriting code for the bootimage.  Any call to a static native
 * method of any class named LibraryImports will be resolved against
 * the blueprint of RuntimeExports
 *
 * FIXME: revisit when / how do we re-build S3Constants from scratch?
 *
 * @author Christian Grothoff, Jan Vitek, Krzysztof Palacz
 **/
public class IRewriter
    extends BootBase
    implements JVMConstants.InvokeSystemArguments, 
	       JVMConstants.Opcodes, 
	       JVMConstants {

    private final static HashMap warningsPrinted_ = new HashMap();
    private final Driver driver_; 
    private boolean isHandlingPragmas = true;

    private S3Blueprint rtExportsBpt_;
    private Oop rtExportsOop_;
    private String executionEngine;

    IRewriter(Driver driver) {
        this.driver_ = driver;
	try {
	    this.executionEngine = driver.arguments.getLongString("engine");
	} catch (IOException e) {
	    throw new Error("impossible!", e);
	}
    }

    public void setHandlingPragmas(boolean flag) {
        this.isHandlingPragmas = flag;
    }

    public void setNativeCallGenerator(NativeCallGenerator ncg) {
	currentNcg_ = ncg;
    }

    public void setRuntimeExports(VM_Address rte) {
        if (rte.isNull()) {
            rtExportsOop_ = null;
            rtExportsBpt_ = null;
        } else {
            rtExportsOop_ = rte.asOop();
            rtExportsBpt_ = (S3Blueprint) rtExportsOop_.getBlueprint();
        }
    }

    private static Selector.Method currentSelector_;
    private NativeCallGenerator currentNcg_;
    
    /**
     * Rewrite one method.
     * @return true iff rewriting changed anything.
     **/
    public boolean rewrite(
        Method method,
        S3ByteCode.Builder builder,
        S3Blueprint curBp,
	boolean hasWriteBarriers,boolean hasReadBarriers) {

        // This is a weird invariant. I'm not sure I like it. But Krzysztof
        // explained it to me this morning. We rewrite everything (even static
        // methods) with respect to the *instance* blueprint. So the instance
        // blueprint's linkset grows to be the union of instance and static
        // requirements--and when the linkset on an instance bp is modified,
        // the mod propagates to the shared state bp. The reverse is not true;
        // a mod to a shared state linkset does not propagate to the instance
        // (and will in fact be clobbered the next time the instance linkset is
        // modified).

        // FIXME For the future, would it be cleaner to have distinct linksets for
        // the instance and shared state bp's?
        // Failing that, why not have them share a linkset instead of
        // synchronizing two copies?  For now, I'll just maintain the wacky
        // status quo. -jcf
        // The status quo is getting harder to maintain now that we are taking even
        // more seriously the difference between instance and shared state types;
        // they have different typenames, making it matter that the blueprint
        // corresponds to the bound selector! -jcf
        // if (curBp.isSharedState())
        //    curBp = (S3Blueprint) curBp.getInstanceBlueprint();
        S3ByteCode code = builder.build();

	d(LOG_REWRITING, code.getSelector() +
	  " has " + code.getAttributes().length + " attributes");
        S3ConstantsBuilder rcpb = (S3ConstantsBuilder) code.getConstantPool();
        CodeFragmentEditor cfe = new CodeFragmentEditor(code, rcpb);
        Selector.Method sel = method.getSelector();

        currentSelector_ = sel;
        
        if ( canSkip(builder, curBp, rcpb, cfe, sel) ) {
	    d(LOG_REWRITING, "skipped " + sel);
            return false;
	}

        cfe.runVisitor(
            new LinearPassController(
                new NonQuickifyingRewriteVisitor(curBp, hasWriteBarriers, hasReadBarriers)));

	d(LOG_REWRITING, sel + (cfe.wasEdited() ? " changed" : " unchanged"));
        // int n = method.getSelector().getDescriptor().getArgumentLength();
        // cfe.commit(builder, n + 1);
	// FIXME: Do we need to commit if wasEdited is false?
        cfe.commit(builder, builder.getMaxStack(), builder.getMaxLocals());
        builder.setConstantPool(rcpb);

        return cfe.wasEdited();
    }

    public static boolean canSkip( S3ByteCode.Builder builder, S3Blueprint curBp,
        S3ConstantsBuilder rcpb, CodeFragmentEditor cfe,  Selector.Method sel) {

	if (PragmaHasNativeSubstitute.declaredBy(sel, curBp)) {
            cfe.commit(builder, builder.getMaxStack(), builder.getMaxLocals());
            builder.setConstantPool(rcpb);
            // builder.setBytes(null);
            return true;
            // FIXME really want to -delete- this method from image
        }
        
        boolean hasPragmaISB = true;
        try {
            if (PragmaTransformCallsiteIR.descendantDeclaredBy(sel, curBp) == null) 
        		hasPragmaISB = false;            
        } catch (PragmaException.UnregisteredPragmaException upe) {
            // unregistered but we can still skip the method
        }
        if (hasPragmaISB) {
            Logger.global.fine( "needn't rewrite "  + sel
        	    	+ " with PragmaTransformCallsiteIR");
            // FIXME really want to -delete- this method from image
            cfe.commit( builder, builder.getMaxStack(), builder.getMaxLocals());
            builder.setConstantPool(rcpb);
            return true;
        }
        return false;
    }

    public void quickify(Method method, S3Blueprint bp) {
	quickify(method,
		 (S3ByteCode) method.getCode(S3ByteCode.KIND),
		 bp,
		 (MemoryManager.the().needsWriteBarrier()
		  && PragmaNoBarriers.descendantDeclaredBy(method.getSelector(),
							   bp) == null),
		 (MemoryManager.the().needsReadBarrier()
		  && PragmaNoBarriers.descendantDeclaredBy(method.getSelector(),
							   bp) == null));
    }

    public boolean quickify(Method method,
			    S3ByteCode code,
			    S3Blueprint curBp,
			    boolean hasWriteBarriers,
			    boolean hasReadBarriers) {
	S3ByteCode.Builder builder = new S3ByteCode.Builder(code);
        S3ConstantsBuilder rcpb = (S3ConstantsBuilder) code.getConstantPool();
        CodeFragmentEditor cfe = new CodeFragmentEditor(code, rcpb);
        Selector.Method sel = method.getSelector();
        currentSelector_ = sel;

        if (canSkip(builder, curBp, rcpb, cfe, sel))
            return false;

        cfe.runVisitor(new LinearPassController(new QuickifyingRewriteVisitor(curBp, hasWriteBarriers,
		       hasReadBarriers)));
        cfe.commit(builder, builder.getMaxStack(), builder.getMaxLocals());
        builder.setConstantPool(rcpb);
	method.addCode(builder.build());

        if (builder.getMaxLocals() == 0)
            Logger.global.warning("MaxLocal == 0 : " + method);

        return cfe.wasEdited();
    }

    public void transcribe(S3ByteCode.Builder builder) {
	S3ByteCode code = builder.build();
        char maxLocals = code.getMaxLocals();
        Method m = code.getMethod();

        if (m.getMode().isStatic()) {
            ++maxLocals;
            LocalVariableTable tbl = null;
            int idx = 0;
            int lvt_idx =
                ByteCodeConstants.attributeNames[Attributes.LocalVariableTable];
            Attribute[] att = code.getAttributes();
            if (att != null)
                att = (Attribute[]) att.clone();
            else
                att = new Attribute[0];
            for (; idx < att.length; idx++)
                if (att[idx].getNameIndex() == lvt_idx) {
                    tbl = (LocalVariableTable) att[idx];
                    break;
                }
            if (tbl != null) {
                int len = tbl.size();
                char[] spc = new char[len + 1];
                char[] vlen = new char[len + 1];
                int[] name = new int[len + 1];
                Descriptor.Field[] type = new Descriptor.Field[len + 1];
                char[] vidx = new char[len + 1];

                for (int i = 0; i < len; i++) {
                    spc[i] = (char) tbl.getStartPC(i);
                    vlen[i] = (char) tbl.getLength(i);
                    name[i] = tbl.getVariableNameIndex(i);
                    type[i] = tbl.getDescriptor(i);
                    vidx[i] = (char) (tbl.getIndex(i) + 1);
                }

                // put local 0 at the end, since j2c never needs to look it up.
                spc[len] = 0;
                vlen[len] = (char) code.getBytes().length;
                name[len] = RepositoryUtils.asUTF("this");
                TypeName gt = m.getDeclaringType().getUnrefinedName();
                if (!gt.isGemeinsam()) 
                    gt =  gt.asScalar().getGemeinsamTypeName();
                
                type[len] = Descriptor.Field.make(gt);
                vidx[len] = 0;

                att[idx] = new LocalVariableTable(spc, vlen, name, type, vidx);
            }
            builder.setAttributes(att);
            code = builder.build();
        }
        S3ConstantsBuilder rcpb = (S3ConstantsBuilder) code.getConstantPool();
        CodeFragmentEditor cfe = new CodeFragmentEditor(code, rcpb);
        final Type.Scalar dclt = m.getDeclaringType().asScalar();
        RepositoryClass rc = dclt.asScalar().getRepositoryClass();

        // Conversion to Ovm specific bytecode
        LinearPassController lpc = new LinearPassController(new Converter());
        lpc.add(new LocalsShifter(1, 0), 
          	new LinearPassController.MethodPicker() {
            public boolean pickMethod(Selector.Method sel) {return dclt.getMethod(sel).getMode().isStatic();}
        	});
            
        lpc.run(cfe);
        cfe.commit(builder, code.getMaxStack(), maxLocals);
        builder.setConstantPool(rcpb);
    }
    
    
    /**
     * This hash map is used to avoid looking at inherited methods again and
     * again.  It maps methods to the respective S3ByteCode.Builders.
     */
    private HashMap done = new HashMap(65536);

    /**
     * @param bpMap: input: map of method to blueprint
     */
    public void rewriteMethod(S3Method method, S3Blueprint bp) {
        S3ByteCode.Builder builder;
        if (done.get(method) != null)  return; // inherited method that we already looked at

        // for native methods, get the generated code; for all others,
        // start with the expanded (but not yet quickened) code.
        if (method.getMode().isAbstract()) return;
 
        if (method.getMode().isNative()) {
            S3ConstantsBuilder cBuilder = (S3ConstantsBuilder)
		((S3Type.Scalar) bp.getType()).getConstantPool();
            builder = currentNcg_.fillInNativeMethod(method, cBuilder);
            if (builder == null) return; // native method that does not get any bytecode!
            builder.setConstantPool(cBuilder);
        } else {
            S3ByteCode code = (S3ByteCode) method.getCode(S3ByteCode.KIND);
            builder = new S3ByteCode.Builder(code);
        }
        transcribe(builder); // this creates Ovm specific bytecode

        boolean hasWriteBarriers = MemoryManager.the().needsWriteBarrier();
        boolean hasReadBarriers  = MemoryManager.the().needsReadBarrier();
	boolean registerFinalizers =
	    (bp.getDomain() instanceof JavaUserDomain
	     && MemoryManager.the().supportsDestructors());
        
        if (PragmaNoBarriers
	    .descendantDeclaredBy(method.getSelector(), bp) != null
	|| bp.getDomain().isExecutive()   // FIXME::: this assume we have no bugs in the ED code. Should be turned on during development    
               ) {
            d(LOG_BARRIERS, "no barriers for ", method);
            hasWriteBarriers=false;
            hasReadBarriers=false;
        }
        
        // Now we have two rewriting passes:
        // (1) Pragma and native call transformations
        // (2) Quickifcation
	try { int i  =0;
	    if (Transaction.the().transactionalMode())
		while (((S3Transaction)Transaction.the()).rewriteTransactional(i++,this, method, builder, bp)); 	    
	    while (rewrite(method, builder, bp, hasWriteBarriers, hasReadBarriers)); // (1)
	  /*  if (Transaction.the().transactionalMode() &&
	        method.toString().equals(Transaction.S3Transaction_doLog)) {
		System.out.println("Got it");
		S3Transaction.rewriteArrays(this,method,builder,bp);
	    }*/
		    
	} catch (Error e) {
	    System.err.println("rewrite of " + method + " failed");
	    throw e;
	}
        if (hasReadBarriers) squirtReadBarriers(method,builder,bp);
        
	squirtPollChecks(method,builder,bp);
	if (registerFinalizers)  registerFinalizers(method,builder,bp);
	S3ByteCode code = builder.build();
	method.addCode(code);
	done.put(method, code);
	
	REWRITTEN_METHOD_COUNT++;
    }

    static public int REWRITTEN_METHOD_COUNT;
    /**
     * This visitor does the transformations such as pragma
     * transformations and native call transformations, but not
     * quickification.
     **/
    public class NonQuickifyingRewriteVisitor extends RewriteVisitor {
        public NonQuickifyingRewriteVisitor(S3Blueprint bp, boolean wbar, boolean rbar) {
            super(bp, wbar, rbar);
        }

        /** @return true if selector represents a native method that can be 
         * rewritten to INVOKENATIVE */
        private boolean handledNativeCall(Method meth,
					  Instruction i) {
            boolean isExecutive = curBp_.getDomain().isExecutive();
            byte[] special = 
		currentNcg_.getSpecialSequence(meth);
            if (special != null) {
                // replace with special opcode!
                Cursor c = cfe.replaceInstruction();
                c.addSpecialSequence(special);
                // call all of the boot_ method that have been 
                // registered from the Driver. This happens after the
                // call to LibraryImports.boot()
                return true;
            }
            return false;
        }

        public void visit(Instruction.PUTFIELD_WITH_BARRIER_REF i) {
        }

        public void visit(Instruction.PUTFIELD i) {
	    try {
		ConstantResolvedInstanceFieldref f =
		    cp.resolveInstanceField(i.getCPIndex(buf));
		if (addWriteBarriers && f.getField() instanceof Field.Reference)
		    cfe.replaceInstruction().addPUTFIELD_WITH_BARRIER_REF(f);
	    } catch (LinkageException e) {
		warn(i, e.getMessage());
	    }
        }

	public void visit(Instruction.PUTSTATIC_WITH_BARRIER_REF i) {
	}

	public void visit(Instruction.PUTSTATIC i) {
	    try {
		ConstantResolvedStaticFieldref f =
		    cp.resolveStaticField(i.getCPIndex(buf));
		if (addWriteBarriers && f.getField() instanceof Field.Reference)
		    cfe.replaceInstruction().addPUTSTATIC_WITH_BARRIER_REF(f);
	    } catch (LinkageException e) {
		warn(i, e.getMessage());
	    }
	}

        //TODO: As long as we do not erase VM_Address (and other Ephemerals)
        // this code will be incorrect.
        public void visit(Instruction.AASTORE i) {
         if (addWriteBarriers)
             cfe.replaceInstruction().addSimpleInstruction(
                 AASTORE_WITH_BARRIER);
     }

	public void visit(Instruction.GETFIELD i) {
	    try {
		cp.resolveInstanceField(i.getCPIndex(buf));
	    } catch (LinkageException e) {
		warn(i, e.getMessage());
	    }
	}

	public void visit(Instruction.GETSTATIC i) {
	    try {
		cp.resolveStaticField(i.getCPIndex(buf));
	    } catch (LinkageException e) {
		warn(i, e.getMessage());
	    }
	}

	public void visit(Instruction.CHECKCAST i) {
	    try {
		cp.resolveClassAt(i.getCPIndex(buf));
	    } catch (LinkageException e) {
		warn(i, e.getMessage());
	    }
	}

	public void visit(Instruction.INSTANCEOF i) {
	    try {
		cp.resolveClassAt(i.getCPIndex(buf));
	    } catch (LinkageException e) {
		warn(i, e.getMessage());
	    }
	}

	public void visit(Instruction.NEW i) {
	    try {
		cp.resolveClassAt(i.getCPIndex(buf));
	    } catch (LinkageException e) {
		warn(i, e.getMessage());
	    }
	}

	public void visit(Instruction.SINGLEANEWARRAY i) {
	    try {
		cp.resolveClassAt(i.getCPIndex(buf));
	    } catch (LinkageException e) {
		warn(i, e.getMessage());
	    }
	}

	public void visit(Instruction.MULTIANEWARRAY i) {
	    try {
		cp.resolveClassAt(i.getCPIndex(buf));
	    } catch (LinkageException e) {
		warn(i, e.getMessage());
	    }
	}

        public void visit(Instruction.AASTORE_WITH_BARRIER i) {
        }
         
         public void visit(Instruction.INVOKEVIRTUAL i) {
            try {
		ConstantResolvedInstanceMethodref vmi =
		    cp.resolveInstanceMethod(i.getCPIndex(buf));

		if (doSystemRewriting_) {
		    Selector.Method sel = vmi.asSelector();
		    S3Blueprint bp = (S3Blueprint) vmi.getStaticDefinerBlueprint();
		    if (handledInlineSubstituteBytecode(sel, bp, i))
			return;
                // FIXME: never rewrite native virtual call sites, do
                // create an body for this method that contains
                // INVOKE_NATIVE
                if (handledNativeCall(vmi.getMethod(), i)) {
                    return;
                }

		}
            } catch (LinkageException e) {
                warn(i, e.getMessage());
            }
        }

        public void visit(Instruction.INVOKESTATIC i) {
            try {
		ConstantResolvedStaticMethodref smi = 
		    getConstants()
		    .resolveStaticMethod(i.getCPIndex(buf));
		if (doSystemRewriting_) {
		    Selector.Method sel = smi.asSelector();
		    // FIXME call resolver first! else access permissions are unchecked!
		    //allowUnsafe(sel, bp);
		    if (handledKernelCall(sel, i)) 
			return;
		    
		    S3Blueprint bp = 
			(S3Blueprint)smi.getSharedState()
			.getBlueprint();
		    try {
			if (handledInlineSubstituteBytecode(sel, bp, i)
			    || handledNativeCall(smi.getMethod(), i)) 
			    return;
		    } catch (Error e) {
			System.err.println("error rewriting " + sel + 
					   " for method " + smi.getMethod());
			throw e;
		    }
		    		    
		    Descriptor.Method rdm = sel.getDescriptor();
		    int argwords = rdm.getArgumentCount() + rdm.getWideArgumentCount();
		    NumberRanges.checkChar(1 + argwords);
		}
            }
	    catch (LinkageException e) {
		warn(i, e.getMessage());
	    }
        }

        public void visit(Instruction.INVOKESPECIAL i) {
            try {
		ConstantResolvedInstanceMethodref smi =
		    getConstants().resolveInstanceMethod(i.getCPIndex(buf));
		if (doSystemRewriting_) {
		    Selector.Method sel = smi.asSelector();

		    // FIXME call resolver first! else access permissions are unchecked!
		    //allowUnsafe(sel, bp);

		    S3Blueprint bp = (S3Blueprint) smi.getStaticDefinerBlueprint();
		    if (handledInlineSubstituteBytecode(sel, bp, i))
			return;
		    // FIXME: remember that privates and constructors take
		    // a `this' pointer.
		    if (handledNativeCall(smi.getMethod(), i)) 
			return;
                 }
            } catch (LinkageException e) { warn(i, e.getMessage());  }
        }

        private boolean isKernelCall(Selector.Method sel) {
	    return sel.getDefiningClass().getShortNameIndex()
		== RepositoryUtils.asUTF("LibraryImports");
        }

        public void visit(Instruction.INVOKEINTERFACE i) {
            try {
		ConstantResolvedInterfaceMethodref imi =
		    getConstants().resolveInterfaceMethod(i.getCPIndex(buf));
		if (doSystemRewriting_) {
		    Selector.Method sel = imi.asSelector();
		    //allowUnsafe(sel, bp);
		    S3Blueprint bp = (S3Blueprint) imi.getStaticDefinerBlueprint();
		    if (handledInlineSubstituteBytecode(sel, bp, i))
			return;
		}
            } catch (LinkageException e) {
                warn(i, e.getMessage());
            }
        }

        private ConstantResolvedInstanceMethodref resolveKernelCall(Selector.Method sel) {
            Descriptor.Method desc = sel.getDescriptor();
            ArrayList newargs = new ArrayList();
            for (int i = 0; i < desc.getArgumentCount(); i++) {
                TypeName tn = desc.getArgumentType(i);
                if (tn.isCompound()) {
                    newargs.add(CommonNames.Oop);
                } else {
                    newargs.add(tn);
                }
            }
            TypeName rettype = desc.getType();
            if (rettype.isCompound()) {
                rettype = CommonNames.Oop;
            }
            Descriptor.Method newdesc =
		Descriptor.Method.make(newargs, rettype);
            int nameIndex = sel.getUnboundSelector().getNameIndex();
            UnboundSelector.Method newsel =
                UnboundSelector.Method.make(nameIndex, newdesc);
            ResolutionInfo ri =
                resolver.resolvePublicVirtualMethod(rtExportsBpt_, newsel);
            // pln("resolved " + sel + " to " + ri.getTarget().getBlueprint());
            if (ri == null) {
                pln("Unable to match " + sel + " to RuntimeExports method");
            }
	    Selector.Method rtesel = 
		Selector.Method.make(newsel,
				     rtExportsBpt_.getType()
				     .getUnrefinedName().asCompound());
	    Method m = resolver
		.resolveInstanceMethod(rtExportsBpt_.getType().asCompound(),
				       rtesel.getUnboundSelector(),
				       null);
	    ConstantResolvedInstanceMethodref imi =
		 ConstantResolvedInstanceMethodref.make(m, ri.getOffset(), rtExportsBpt_);
            return imi;
        }

	private boolean handledKernelCall(Selector.Method sel,
					  Instruction.Invocation i) {
	    if (isKernelCall(sel)) {
		//System.err.println("Q : handling kernel call : " + sel);
		if (rtExportsBpt_ == null) {
		    warn(i, "can't rewrite kernel call: ");
		    return true;
		}
		ConstantResolvedInstanceMethodref imr = 
		    resolveKernelCall(sel);
 
		Cursor c = cfe.replaceInstruction();
		Descriptor.Method rdm = sel.getDescriptor();
		int argwords = rdm.getArgumentCount() + rdm.getWideArgumentCount();
		char rollDepth = NumberRanges.checkChar(1 + argwords);

  		c.addResolvedRefLoadConstant(rtExportsOop_);
  		c.addRoll(rollDepth, (byte) 1);
		c.addINVOKEVIRTUAL(imr);
		if (sel.getDescriptor().getType().isCompound())
		    c.addFiat(sel.getDescriptor().getType());
		return true;
            }
            return false;
        }

        /** If <em>sel</em> and <em>bp</em> identify a method that declares 
         * (a descendant of) {@link s3.util.PragmaTransformCallsiteIR},  remove the 
         * current instruction, replace it with the bytecode sequence associated with 
         * the pragma, and return <code>true</code>, otherwise return <code>false</code>.
         *  @param sel selector of a method
         *  @param bp blueprint of the method's declaring class (caller ensures) 
         *  @return <code>true</code> if a pragma was found and processed,
         * otherwise <code>false</code>.*/
        private boolean handledInlineSubstituteBytecode(
            Selector.Method sel,
            Blueprint bp,
            Instruction.Invocation instr) {
            
            if (!isHandlingPragmas) {
                return false;
            }
            Object sub;
            try {
                sub = PragmaTransformCallsiteIR.descendantDeclaredBy(sel, bp);
            } catch (PragmaException.UnregisteredPragmaException upe) {
                d(
                    sel
                        + " with unregistered InlineSubstituteBytecode"
                        + " behavior is invoked in "
                        + buf.getSelector());
                sub =
                    PragmaTransformCallsiteIR.DeadRewriter.make(
                        "Method has unregistered pragma: ");
            }
            if (sub == null)
                return false;
            int pc = buf.getPC();

            if (sub instanceof byte[]) {
		cfe.replaceInstruction(pc).addSpecialSequence((byte[]) sub);
                return true;
            }
            // rewrite() calls cfe.getCursorAfterMarker( pc) again; if I understand
            // correctly, that will return the same cursor obtained above and
            // positioned in the same place (after ROLL/POP if used), so it's ok.
            ((PragmaTransformCallsiteIR.Rewriter) sub).rewrite(
                curBp_,
                instr,
                bp,
                sel,
                cfe,
                resolver);
            return true;
        }

        public void visit(Instruction.LDC i) {
            try {
                i.getCPIndex(buf); // advance PC
                byte tag = i.getCType(buf, cp);
                switch (tag) {
                    case JVMConstants.CONSTANT_Integer :
                    case JVMConstants.CONSTANT_Float :
                        cfe.replaceInstruction(getPC()).addQuickLdc( i.getValue(buf, cp));
                        break;
                    case JVMConstants.CONSTANT_Reference :
                    case JVMConstants.CONSTANT_String :
                    case JVMConstants.CONSTANT_SharedState :
                    case JVMConstants.CONSTANT_Binder :
                        break;
                    default : warn(i, "did not expect constant type " + tag);
                }
            } catch (ovm.util.OVMError e) { warn(i, e.getMessage()); }
        }

        public void visit(Instruction.LDC_W i) {
            try {
                byte tag = i.getCType(buf, cp);
                switch (tag) {
                    case JVMConstants.CONSTANT_Integer :
                    case JVMConstants.CONSTANT_Float :
                        cfe.replaceInstruction().addQuickLdc(i.getValue(buf, cp));
                        break;
                    case JVMConstants.CONSTANT_Reference :
                    case JVMConstants.CONSTANT_String :
                    case JVMConstants.CONSTANT_SharedState :
                    case JVMConstants.CONSTANT_Binder :
                        break;
                    default :
                        warn(i, "did not expect constant type " + tag);
                }
            } catch (ovm.util.OVMError e) {
                warn(i, e.getMessage());
            }
        }

        public void visit(Instruction.LDC2_W i) {
            cfe.replaceInstruction(getPC()).addQuickLdc(i.getValue(buf, cp));
        }

    } // end NonQuickifyingRewriteVisitor


    
    private static ReadBarrierSquirter readBarrierSquirter_ =
     new ReadBarrierSquirter();

    private void squirtReadBarriers(S3Method method,
                                  S3ByteCode.Builder builder,
                                  S3Blueprint curBp) {
	S3ByteCode code=builder.build();
   	S3ConstantsBuilder rcpb =
	    (S3ConstantsBuilder) ( (S3Constants) code.getConstantPool())
	    .getBuilder();
        CodeFragmentEditor cfe = new CodeFragmentEditor(code, rcpb);
        InstructionBuffer codeBuf = cfe.getOriginalCode();
        codeBuf.rewind();
        readBarrierSquirter_.beginEditing(codeBuf, cfe);
        try {
            while (codeBuf.hasRemaining()) {
             readBarrierSquirter_.visitAppropriate(codeBuf.get());
            }
        } finally {
            readBarrierSquirter_.endEditing();
        }

        cfe.commit(builder, builder.getMaxStack(), builder.getMaxLocals());
        builder.setConstantPool(rcpb.build());
    }

    public static class ReadBarrierSquirter extends InstructionEditVisitor {
     
	public void visit(Instruction.AALOAD i) {
	    Cursor c=cfe.getCursorAfterMarker(getPC());
	    c.addSimpleInstruction(DUP);
	    c.addSimpleInstruction(READ_BARRIER);        
	}

	public void visit(Instruction.GETFIELD i) {
	    Cursor c=cfe.getCursorAfterMarker(getPC());
	    Descriptor.Field f = i.getSelector(buf, cp).getDescriptor();
	    if (f.isReference()
		&& f.getType() != JavaNames.ovm_core_services_memory_VM_Address
		&& f.getType() != JavaNames.ovm_core_services_memory_VM_Word) {
		c.addSimpleInstruction(DUP);
		c.addSimpleInstruction(READ_BARRIER);        
	    }
	}

	public void visit(Instruction.GETSTATIC i) {
	    Cursor c=cfe.getCursorAfterMarker(getPC());
	    Descriptor.Field f = i.getSelector(buf, cp).getDescriptor();
	    if (f.isReference()
		&& f.getType() != JavaNames.ovm_core_services_memory_VM_Address
		&& f.getType() != JavaNames.ovm_core_services_memory_VM_Word) {
		c.addSimpleInstruction(DUP);
		c.addSimpleInstruction(READ_BARRIER);        
	    }
	}

    }
     /**
     * Visitor that actually adds poll checks.
     */
    public static class PollCheckSquirter extends InstructionEditVisitor {
        private void addPOLLCHECKHere() {
            cfe.getCursorBeforeMarker(getPC()).addPOLLCHECK();
        }
        public void visit(Instruction.Switch i) {
            // code taken our of J2c's BBSpec
            int[] target = i.getTargets(buf);
            int dflt = i.getDefaultTarget(buf);
            boolean back = dflt < 0;
            for (int idx = 0; idx < target.length && !back; idx++)
                if (target[idx] < 0)
                    back = true;
            if (back) {
                //ovm.core.OVMBase.d("adding poll check on Switch!");
                addPOLLCHECKHere();
            }
        }
        public void visit(Instruction.ConditionalJump i) {
            if (i.getBranchTarget(buf) < 0) {
                //ovm.core.OVMBase.d("adding poll check on ConditionalJump!");
                addPOLLCHECKHere();
            }
        }
        public void visit(Instruction.UnconditionalJump i) {
            if (i.getTarget(buf) < 0) {
                //ovm.core.OVMBase.d("adding poll check UnconditionalJump!");
                addPOLLCHECKHere();
            }
        }
    }

    private static PollCheckSquirter pollCheckSquirter_ =
        new PollCheckSquirter();

    private static final UnboundSelector.Method setReschedulingEnabledSel
	= RepositoryUtils.selectorFor
	("Lovm/services/threads/UserLevelThreadManager;",
	 "setReschedulingEnabled:(Z)Z").asMethod().getUnboundSelector();

    // UserLevelThreadManager.setReschedulingEnabled() is an interface
    // method, but since we know the exact type of the ThreadManager
    // singleton, we can emit a virtual call
    private static ConstantResolvedInstanceMethodref setReschedulingEnabled;
    
    public static class RestoreEnabledSquirter extends InstructionEditVisitor {
	int enabledVar;
	Object tm;
	private RestoreEnabledSquirter(int enabledVar, Object tm) {
	    this.enabledVar = enabledVar;
	    this.tm = tm;
	}
	
	private void restoreEnabled(Cursor c) {
	    c.addResolvedRefLoadConstant(tm);
	    c.addILoad((char) enabledVar);
	    c.addINVOKEVIRTUAL(setReschedulingEnabled);
	    c.addSimpleInstruction(POP);
	}

	public void visit(Instruction.ReturnValue _) {
	    restoreEnabled(cfe.getCursorBeforeMarker(getPC()));
	}
	public void visit(Instruction.RETURN _) {
	    restoreEnabled(cfe.getCursorBeforeMarker(getPC()));
	}
    }
	    
    /**
     * Adds POLLCHECK instructions at the beginning of the method
     * and immediately prior to backwards branches.
     */
    private void squirtPollChecks(S3Method method,
                                  S3ByteCode.Builder builder,
                                  S3Blueprint curBp) {
	if (PragmaAtomic.descendantDeclaredBy(method.getSelector(), curBp) != null) {
	    d(LOG_POLLCHECKS, method + " is atomic");
	    ThreadServicesFactory tsf = (ThreadServicesFactory)
		ThreadServiceConfigurator.config.
		getServiceFactory(ThreadServicesFactory.name);
	    UserLevelThreadManager tm
		= (UserLevelThreadManager) tsf.getThreadManager();
	    if (setReschedulingEnabled == null) {
		S3Blueprint bp = (S3Blueprint)
		    VM_Address.fromObject(tm).asOop().getBlueprint();
		MemberResolver resolver =
		    DomainDirectory.getExecutiveDomain().getMemberResolver();
		ResolutionInfo ri = resolver.
		    resolvePublicVirtualMethod(bp, setReschedulingEnabledSel);
		Method m = resolver.
		    resolveInstanceMethod(bp.getType().asCompound(),
					  setReschedulingEnabledSel,
					  null);
		setReschedulingEnabled =
		    ConstantResolvedInstanceMethodref.make(m, ri.getOffset(), bp);
	    }
	    S3ByteCode code = builder.build();
	    S3ConstantsBuilder rcpb = (S3ConstantsBuilder) code.getConstantPool();
	    int enabledVar = code.getMaxLocals();
	    CodeFragmentEditor cfe = new CodeFragmentEditor(code, rcpb);
	    InstructionBuffer codeBuf = cfe.getOriginalCode();
	    Cursor c = cfe.getCursorBeforeMarker(0);
	    c.addResolvedRefLoadConstant(tm);
	    c.addLoadConstant(0);
	    c.addINVOKEVIRTUAL(setReschedulingEnabled);
	    c.addIStore((char) enabledVar);
	    Marker enableSaved = c.addMarker();
	    RestoreEnabledSquirter squirt = new RestoreEnabledSquirter(enabledVar, tm);
	    codeBuf.rewind();
	    squirt.beginEditing(codeBuf, cfe);
	    int lastPC = 0;
	    Instruction last = null;
            while (codeBuf.hasRemaining()) {
		last = codeBuf.get();
		lastPC = codeBuf.getPC();
                squirt.visitAppropriate(last);
            }

	    ExceptionHandlerList eh = cfe.getExceptionHandlers();
	    for (ExceptionHandlerList n = eh.next();
		 n != null;
		 eh = n, n = eh.next())
		;
	    c = cfe.getCursorAfterMarker(lastPC);
	    Marker m = c.addMarker();
	    eh.insertAfter(enableSaved, m, m, JavaNames.java_lang_Throwable);
	    squirt.restoreEnabled(c);
	    c.addSimpleInstruction(ATHROW);
	    cfe.commit(builder,
		       (char) (code.getMaxStack() + 2),
		       (char) (enabledVar + 1));

	    return;
	}
        if (PragmaNoPollcheck.descendantDeclaredBy(method.getSelector(),
						   curBp) != null) {
            d(LOG_POLLCHECKS, "squirtPollChecks() skipping ", method);
            return;
        }
        
	S3ByteCode code=builder.build();
   	S3ConstantsBuilder rcpb =
	    (S3ConstantsBuilder) code.getConstantPool();
	CNT++;
        CodeFragmentEditor cfe = new CodeFragmentEditor(code, rcpb);
	if (!PragmaInline.declaredBy(method.getSelector(), curBp)) {
	    // Skip pollcheck in prologue if this method shouldn't
	    // have a prologue to begin with.
	   if (// FIXME::: We are getting rid of pollchecks in leaf methods.. Transaction.the().transactionalMode() &&
		   !method.getMode().isNative()) {
	       Optimizer.SimpleSequenceVisitor ssv = 	new Optimizer.SimpleSequenceVisitor();
	       ssv.run(method);
	       if (!ssv.hasBranch) {
		   NOB++;
		   if(!ssv.hasAlloc) NOA++;
		   if (!ssv.hasCall) NOC++;
		   if (!ssv.hasCall && !ssv.hasAlloc) NONE++;
	       }
	    if (!ssv.clean)
		cfe.getCursorBeforeMarker(0).addPOLLCHECK();
	   } else cfe.getCursorBeforeMarker(0).addPOLLCHECK();
	} else
            d(LOG_POLLCHECKS, "eliding entry pollcheck on inline method: ", method);

        InstructionBuffer codeBuf = cfe.getOriginalCode();
        codeBuf.rewind();
        pollCheckSquirter_.beginEditing(codeBuf, cfe);
        try {
            while (codeBuf.hasRemaining()) {
                pollCheckSquirter_.visitAppropriate(codeBuf.get());
            }
        } finally {
            pollCheckSquirter_.endEditing();
        }

        cfe.commit(builder, builder.getMaxStack(), builder.getMaxLocals());

        builder.setConstantPool(rcpb); // not necessary for us?
    }
    public static int CNT, NOB, NOC, NOA, NONE;

    public class FinalizerRegistrar extends InstructionEditVisitor
    {
	// FIXME: We really want to register the finalizer just before
	// the constructor invocation, rather than just after the NEW
	// instruction.  However, there is no easy way to determine
	// whether a call to <init> comes from a new expression, or a
	// super(); thingy.  (super() thingies are not statements, but
	// special parts of the constructor body).
	public void visit(Instruction.NEW i) {
	    try {
		S3Constants s3cp = (S3Constants) cp;
		Type.Scalar t = (Type.Scalar)
		    s3cp.resolveClassAt(i.getCPIndex(buf)).getType();
		while (t.getSuperclass() != null) {
		    if (t.getMethod(JavaNames.FINALIZE) != null)
			break;
		    t = t.getSuperclass();
		}
		if (t.getSuperclass() != null) {
		    //System.err.println("finalizer in " + t);
		    Cursor c = cfe.getCursorAfterMarker(getPC());
		    c.addSimpleInstruction(DUP);
		    c.addResolvedRefLoadConstant(rtExportsOop_);
		    c.addSimpleInstruction(SWAP);
		    MemberResolver resolver = t.getDomain().getMemberResolver();
		    ResolutionInfo ri =
			resolver.resolvePublicVirtualMethod(rtExportsBpt_,
							    JavaNames.REGISTER_FINALIZER);
		    Method m = resolver
			.resolveInstanceMethod(rtExportsBpt_.getType().asCompound(),
					       JavaNames.REGISTER_FINALIZER,
					       null);
		    ConstantResolvedInstanceMethodref imi =
			ConstantResolvedInstanceMethodref.make(m, ri.getOffset(), rtExportsBpt_);
		    c.addINVOKEVIRTUAL(imi);
		}
		// the broken, yet somehow more correct way: rewrite
		// calls to <init> after NEW instructions.  This code
		// rewrites all calls to <init>, which is wrong.
// 		Selector.Method sel = i.getSelector(buf, cp);
// 		if (sel.getNameIndex() != JavaNames.INIT)
// 		    return;
		
// 		ConstantResolvedInstanceMethodref smi =
// 		    ((S3Constants) cp).resolveInstanceMethod(i.getCPIndex(buf));
// 		Type.Scalar t = (Type.Scalar)
// 		    smi.getStaticDefinerBlueprint().getType();
// 		while (t.getSuperclass() != null) {
// 		    if (t.getMethod(JavaNames.FINALIZE) != null)
// 			break;
// 		    t = t.getSuperclass();
// 		}
// 		if (t.getSuperclass() != null) {
// 		    System.err.println("finalizer in " + t);
// 		    // We have successfully evaluated all ctor
// 		    // arguments, now it is safe to call
// 		    // registerFinalizer:
// 		    //
// 		    // roll instance to top
// 		    // dup
// 		    // push RTE reference
// 		    // swap
// 		    // call registerFinalizer
// 		    // roll instance to bottom
// 		    // original bytecode
// 		    int length = i.getArgumentLengthInWords(buf, cp);
// 		    Cursor c = cfe.getCursorBeforeMarker(getPC());

// 		    if (length > 1)
// 			c.addRoll((char) length, (byte) -1);
// 		    c.addSimpleInstruction(DUP);
// 		    c.addResolvedRefLoadConstant(rtExportsOop_);
// 		    c.addSimpleInstruction(SWAP);
// 		    MemberResolver resolver = t.getDomain().getMemberResolver();
// 		    ResolutionInfo ri =
// 			resolver.resolvePublicVirtualMethod(rtExportsBpt_,
// 							    JavaNames.REGISTER_FINALIZER);
// 		    Method m = resolver
// 			.resolveInstanceMethod(rtExportsBpt_.getType().asCompound(),
// 					       JavaNames.REGISTER_FINALIZER,
// 					       null);
// 		    ConstantResolvedInstanceMethodref imi =
// 			ConstantResolvedInstanceMethodref.make(m, ri.getOffset(), rtExportsBpt_);
// 		    c.addINVOKEVIRTUAL(imi);
// 		    if (length > 1)
// 			c.addRoll((char) length, (byte) 1);
// 		}
	    } catch (LinkageException _) {
		// don't report error again.
	    }
	}
    }

    private FinalizerRegistrar finalizerRegistrar = new FinalizerRegistrar();

    private void registerFinalizers(S3Method method,
				    S3ByteCode.Builder builder,
				    S3Blueprint curBp) {
	S3ByteCode code=builder.build();
   	S3ConstantsBuilder rcpb =
	    (S3ConstantsBuilder) code.getConstantPool();

        CodeFragmentEditor cfe = new CodeFragmentEditor(code, rcpb);

        InstructionBuffer codeBuf = cfe.getOriginalCode();
        codeBuf.rewind();
        finalizerRegistrar.beginEditing(codeBuf, cfe);
        try {
            while (codeBuf.hasRemaining()) {
                finalizerRegistrar.visitAppropriate(codeBuf.get());
            }
        } finally {
            finalizerRegistrar.endEditing();
        }

        cfe.commit(builder, builder.getMaxStack(), builder.getMaxLocals());

        builder.setConstantPool(rcpb); // not necessary for us?
    }

    /**
     * This visitor does the quickification.
     **/
    public class QuickifyingRewriteVisitor extends RewriteVisitor {
	private Instruction[] iset;
        public QuickifyingRewriteVisitor(S3Blueprint bp,
					 boolean wbar,boolean rbar) {
            super(bp, wbar, rbar);
	    iset = InstructionSet.SINGLETON.getInstructions();
        }

        public void visit(Instruction.NEW i) {
            if (!isExecutive_)
                return; // do NOT quickify in user domain!
            try {
		int idx = i.getCPIndex(buf);
		getConstants().resolveClassAt(idx);
                cfe.replaceInstruction().addQuickOpcode(NEW_QUICK, 
							(char)idx);
            } catch (LinkageException e) {
                warn(i, e.getMessage() + " in " + getSelector());
		// no need to expand, size(NEW) == size(NEW_QUICK)
            }
        }
        public void visit(Instruction.NEWARRAY i) {
	    warn(i, "NEWARRAY should have been rewritten to SINGLEANEWARRAY");
        }
	public void visit(Instruction.SINGLEANEWARRAY i) {
	    try {
		int idx = i.getCPIndex(buf);
		getConstants().resolveClassAt(idx);
                cfe.replaceInstruction()
		    .addQuickOpcode(ANEWARRAY_QUICK, (char)idx);
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		// no need to expand, 
		// size(SINGLEANEWARRAY) == size(ANEWARRAY_QUICK)
            }
	}
        public void visit(Instruction.MULTIANEWARRAY i) {
            try {
		int idx = i.getCPIndex(buf);
		getConstants().resolveClassAt(idx);
                cfe.replaceInstruction()
		    .addMULTIANEWARRAY_QUICK((char)idx,
					     i.getDimensions(buf));
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		// no need to expand, 
		// size(MULTIANEWARRAY) == size(MULTIANEWARRAY_QUICK)
            }
        }
        public void visit(Instruction.LDC i) {
            try {
                char idx = (char) i.getCPIndex(buf);
                byte tag = i.getCType(buf, cp);
                switch (tag) {
                    case JVMConstants.CONSTANT_Reference :
                        // already done...
                        break;
                    case JVMConstants.CONSTANT_Integer :
                    case JVMConstants.CONSTANT_Float :
			throw new Error
			    ("Should have been rewritten "
			     + "by NonQuickifyingRewriteVisitor");
                    case JVMConstants.CONSTANT_String :
                    case JVMConstants.CONSTANT_SharedState :
                    case JVMConstants.CONSTANT_Binder :
                        if (isExecutive_) {
                            cfe.replaceInstruction(getPC()).addQuickLdc(
                                ((S3Constants) getConstantPool())
                                 .resolveConstantAt(idx));
                        }
                        break;
                    default :
                        warn(i, "did not expect constant type " + tag);
                }
            } catch (ovm.util.OVMError e) {
                warn(i, e.getMessage());
            }
        }
        public void visit(Instruction.LDC_W i) {
            try {
                char idx = (char) i.getCPIndex(buf);
                byte tag = i.getCType(buf, cp);
                switch (tag) {
                    case JVMConstants.CONSTANT_Reference :
                        // already done...
                        break;
                    case JVMConstants.CONSTANT_Integer :
                    case JVMConstants.CONSTANT_Float :
			throw new Error
			    ("Should have been rewritten "
			     + "by NonQuickifyingRewriteVisitor");
                    case JVMConstants.CONSTANT_String :
                    case JVMConstants.CONSTANT_SharedState :
                    case JVMConstants.CONSTANT_Binder :
                        if (isExecutive_) {
                            cfe.replaceInstruction(getPC()).addQuickLdc(
                                ((S3Constants) getConstantPool())
                                        .resolveConstantAt(idx));
                        }
                        break;
                    default :
                        warn(i, "did not expect constant type " + tag);
                }
            } catch (ovm.util.OVMError e) {
                warn(i, e.getMessage());
            }
        }
        public void visit(Instruction.LDC2_W i) {
	    throw new Error
		("Should have been rewritten by NonQuickifyingRewriteVisitor");
        }

        public void visit(Instruction.ANEWARRAY i) {
	    warn(i, "ANEWARRAY should have been rewritten to SINGLEANEWARRAY");
        }

         public void visit(Instruction.GETFIELD i) {
            try {
		ConstantResolvedInstanceFieldref ifi =
		    getConstants()
		    .resolveInstanceField(i.getCPIndex(buf));
                S3Blueprint bp = (S3Blueprint)ifi.getStaticDefinerBlueprint();
                Selector.Field sel = ifi.asSelector();
		Field rinfo = ifi.getField();
                if (rinfo == null) {
                    warnMissing(i, bp, sel);
                    return;
                }

                Cursor c = cfe.replaceInstruction();
     		    rinfo.addGetfieldQuick(c);
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		// no need to expand, 
		// size(GETFIELD) == size(GETFIELD_QUICK)
            }
        }
        public void visit(Instruction.PUTFIELD i) {
           try {
		ConstantResolvedInstanceFieldref ifi =
		    getConstants()
		    .resolveInstanceField(i.getCPIndex(buf));
                S3Blueprint bp = (S3Blueprint)ifi.getStaticDefinerBlueprint();
                Selector.Field sel = ifi.asSelector();
                Field resinfo = ifi.getField();
                if (resinfo == null) {
                    warnMissing(i, bp, sel);
                    return;
                }
                Cursor c = cfe.replaceInstruction();
		if (i instanceof Instruction.PUTFIELD_WITH_BARRIER_REF) {
		    resinfo.addPutfieldQuickWithBarrier(c); 
		} else {
		    resinfo.addPutfieldQuick(c);
		}
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		// no need to expand, 
		// size(PUTFIELD) == size(PUTFIELD_QUICK)
            }
        }
        public void visit(Instruction.GETSTATIC i) {
            try {
		ConstantResolvedStaticFieldref sfi = 
		    getConstants().resolveStaticField(i.getCPIndex(buf));
		Field field = sfi.getField();
                Cursor c = cfe.replaceInstruction();
		if (isExecutive_)
		    c.addLOAD_SHST_FIELD_QUICK(i.getCPIndex(buf));
		else
		    c.addLOAD_SHST_FIELD(i.getCPIndex(buf));
		Selector.Field sel = sfi.asSelector();				
		TypeName tn = sel.getDescriptor().getType();
		field.addGetfieldQuick(c);
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		// expand
		padd(iset[LOAD_SHST_FIELD].size(null) +
		     iset[GETFIELD_QUICK].size(null),
		     i);
            }
        }

        public void visit(Instruction.PUTSTATIC i) {
            try {
		ConstantResolvedStaticFieldref sfi = 
		    getConstants()
		    .resolveStaticField(i.getCPIndex(buf));
		Field field = sfi.getField();
                Selector.Field sel = sfi.asSelector();
                Cursor c = cfe.replaceInstruction();
		if (isExecutive_)
		    c.addLOAD_SHST_FIELD_QUICK(i.getCPIndex(buf));
		else
		    c.addLOAD_SHST_FIELD(i.getCPIndex(buf));
		if (sel.getDescriptor().isWidePrimitive()) {
		    c.addRoll((char)3, (byte)1);
		} else {
		    c.addSimpleInstruction(SWAP); // eqv roll(2,1)
		}
		TypeName tn = sel.getDescriptor().getType();
		if (addWriteBarriers
		    && tn != JavaNames.ovm_core_services_memory_VM_Address
		    && tn != JavaNames.ovm_core_services_memory_VM_Word)
		    field.addPutfieldQuickWithBarrier(c);
		else
		    field.addPutfieldQuick(c);
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		// expand
		padd(iset[LOAD_SHST_FIELD].size(null) +
		     iset[ROLL].size(null) + // assume size(SWAP) < size(ROLL)
		     iset[GETFIELD_QUICK].size(null),
		     i);
            }
        }

        public void visit(Instruction.INVOKEVIRTUAL i) {
            try {
		ConstantResolvedInstanceMethodref vmi = getConstants()
                             .resolveInstanceMethod(i.getCPIndex(buf));
                Selector.Method sel = vmi.asSelector();
                cfe.replaceInstruction()
                  .addINVOKEVIRTUAL_QUICK(new S3MemberResolver.RInfo(vmi.getOffset()), sel);
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		padd(iset[INVOKEVIRTUAL_QUICK].size(null), i); // INVOKEVIRTUAL_QUICK
            }
        }

        public void visit(Instruction.INVOKESPECIAL i) {
            try {
		int cpindex = i.getCPIndex(buf);
		ConstantResolvedInstanceMethodref smi = 
		     getConstants().resolveInstanceMethod(cpindex);
                Selector.Method sel = smi.asSelector();
                curIBp.getType(); // FIXME: does this have any sideeffect? if not delete--jv

                boolean isSuperCall = ! smi.isNonVirtual;
                ResolutionInfo rInfo = new S3MemberResolver.RInfo(smi.getOffset());
                if (isSuperCall) {
		// Case 1. a super call (via vtable)
                    cfe.replaceInstruction().addINVOKESUPER_QUICK(rInfo, cpindex, sel);
                } else {
                    // Case 2. a constructor / private call (via nvtable)
                    cfe.replaceInstruction().addINVOKENONVIRTUAL2_QUICK(rInfo, cpindex, sel);
                }
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		// expand
		padd(iset[INVOKENONVIRTUAL2_QUICK].size(null),  i); 
                // assume size(INVOKENONVIRTUAL2_QUICK) == size(INVOKESUPER_QUICK)
            }
        }

        public void visit(Instruction.INVOKEINTERFACE i) {
            try {
		ConstantResolvedInterfaceMethodref imi = 
		    getConstants().resolveInterfaceMethod(i.getCPIndex(buf));
                ResolutionInfo rInfo = new S3MemberResolver.RInfo( imi.getOffset());
                    // FIXME:  yes the above is ugly, RInfo has go to go... -jv
                cfe.replaceInstruction().addINVOKEINTERFACE_QUICK(rInfo, imi.asSelector());
            } catch (LinkageException e) {
                warn(i, e.getMessage());
                // expand
                padd(iset[INVOKEINTERFACE_QUICK].size(null), i);
            }
        }
        public void visit(Instruction.INVOKESTATIC i) {
            Selector.Method sel = i.getSelector(buf, cp);
            int argLength = sel.getDescriptor().getArgumentCount()
                + sel.getDescriptor().getWideArgumentCount() 
		+ 1; //shst
 	    int cpindex = i.getCPIndex(buf);
            try {
		ConstantResolvedStaticMethodref smi = 
		    getConstants()
		    .resolveStaticMethod(i.getCPIndex(buf));
		ResolutionInfo rinfo = 
                 new S3MemberResolver.RInfoWithOop(smi.getSharedState(), smi.getOffset());
		Cursor c = cfe.replaceInstruction();
		if (isExecutive_)
		    c.addLOAD_SHST_METHOD_QUICK(cpindex);
		else
		    c.addLOAD_SHST_METHOD(cpindex);
		c.addRoll((char)argLength, (byte)1);
		c.addINVOKENONVIRTUAL_QUICK(rinfo, smi.asSelector());
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		// expand
		padd(iset[LOAD_SHST_METHOD].size(null) +
		     iset[ROLL].size(null) + // assume size(SWAP) < size(ROLL)
		     iset[INVOKENONVIRTUAL_QUICK].size(null),
		     i);
            }
        }
        public void visit(Instruction.INSTANCEOF i) {
            try {
		int idx = i.getCPIndex(buf);
		getConstants().resolveClassAt(idx);
                cfe.replaceInstruction()
		    .addQuickOpcode(INSTANCEOF_QUICK, (char)idx);
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		// no need to expand, 
		// size(INSTANCEOF) == size(INSTANCEOF_QUICK)
            }
        }

        public void visit(Instruction.CHECKCAST i) {
            try {
		int idx = i.getCPIndex(buf);
		getConstants().resolveClassAt(idx);
                cfe.replaceInstruction()
		    .addQuickOpcode(CHECKCAST_QUICK, (char)idx);
            } catch (LinkageException e) {
                warn(i, e.getMessage());
		// no need to expand, 
		// size(INSTANCEOF) == size(INSTANCEOF_QUICK)
            }

        }

	private void padd(int t,
			  Instruction i) {
	    int s = i.size(buf);

	    if (s != t) {
		Cursor c = cfe.getCursorBeforeMarker(getPC()+s);
		if (s < t)
		    for (int j=s;j<t;j++)
			c.addSimpleInstruction(NOP);
		else
		    throw new Error("Quick instruction may not be smaller than original bytecode!");		
	    }
	}

    } // end QuickifyingRewriteVisitor

    /**
     * Refactored into the above two visitors.
     **/
    public abstract class RewriteVisitor
        extends InstructionEditVisitor
        implements JVMConstants.Opcodes, Ephemeral.Void {
        protected final S3Blueprint curBp_;
        final S3Blueprint curIBp;
        protected MemberResolver resolver;
        protected final boolean isExecutive_;
	protected final boolean isSystemTypeContext_;
	protected final boolean doSystemRewriting_;
	protected final boolean addWriteBarriers;
	protected final boolean addReadBarriers;
    protected RewriteVisitor(S3Blueprint bp, boolean wbar, boolean rbar) {
            OVMassert(bp != null);
            this.curBp_ = bp;
            curIBp =
                (bp.isSharedState()
                    ? (S3Blueprint) bp.getInstanceBlueprint()
                    : bp);

            S3Domain dom = (S3Domain) curBp_.getType().getDomain();
            this.resolver = dom.getMemberResolver();
	    isExecutive_ = dom.isExecutive();
	    isSystemTypeContext_ = isInSystemTypeContext();
	    doSystemRewriting_ = 
		isSystemTypeContext_ 
		|| driver_.arguments.getBoolean("system-rewrite-application-code");
	    addWriteBarriers = wbar;
	    addReadBarriers = rbar;
        }

	protected S3Blueprint rtExportsBpt() { return rtExportsBpt_; }
	protected Oop rtExportsOop() { return rtExportsOop_; }
	
	private boolean isInSystemTypeContext() {
	    try {
		Type curType = curBp_.getType();
		S3Domain cdom = (S3Domain)curBp_.getDomain();
		Type.Context ctx = cdom.getSystemTypeContext();
		Type aType = ctx.typeFor(curType.getUnrefinedName());
		return aType == curType;
	    } catch (LinkageException e) {
		return false;
	    }
	}

        //--------------------------------------------------------
        //---------------------- helpers -------------------------
        //--------------------------------------------------------

        /** If <em>sel</em> and <em>bp</em> identify a method that declares 
         * (a descendant of) {@link ovm.util.PragmaUnsafe},  check that the
         * current class has declared to implement {@link ovm.util.PragmaUnsafe}. 
         */
        protected boolean allowUnsafe(Selector.Method sel, Blueprint bp) {
            if (true || !isHandlingPragmas) // FIXME re?nable some day
                return true;
            if (PragmaUnsafe.declaredBy(sel, bp)) {
                try {
                    S3Domain domain = (S3Domain) driver_.getCurrentDomain();
                    Blueprint unsafe =
                        driver_.blueprintFor(
                            ovm.util.UnsafeAccess.class,
                            domain);
                    if (!curBp_.getType().isSubtypeOf(unsafe.getType())) {
                        pln(
                            "Attempting to access  unsafe method: "
                                + sel
                                + "\n\tfrom:"
                                + curBp_);
                        return false;
                    }
                } catch (LinkageException e) {
                    fail(e);
                }
            }
            return true;
        }

        protected void warnMissing(
            Instruction i,
            Blueprint bpt,
            Selector sel) {
            if (sel instanceof Selector.Method) {
                Selector.Method msel = (Selector.Method) sel;
                warn(
                    i,
                    JavaFormat._.format(bpt.getName())
                        + " lacks "
                        + msel.getUnboundSelector()
                        + " in "
                        + buf.getSelector());
            } else {
                Selector.Field msel = (Selector.Field) sel;
                warn(
                    i,
                    JavaFormat._.format(bpt.getName())
                        + " lacks "
                        + msel.getUnboundSelector()
                        + " in "
                        + buf.getSelector());
            }
        }

	protected S3Constants cp;
	public void beginEditing(InstructionBuffer b, CodeFragmentEditor cfe) {
	    super.beginEditing(b, cfe);
	    cp = (S3Constants) cfe.getConstantsEditor();
	}
	protected S3Constants getConstants() {
	    return cp;
	}


    } // end RewriteVisitor

  
    public static void warn(Instruction i, String s) {
        if ((logMask & LOG_REWRITE_ERRORS) == 0)
            return;
        String message =
            "Rewriting " + i.getName() + ": " + s + " in " + currentSelector_;
        if (warningsPrinted_.get(message) != null)
            return;
        warningsPrinted_.put(message, message);
        Logger.global.warning(message);
    }

} // end of IRewriter
