FileDocCategorySizeDatePackage
EVMMethodInfo.javaAPI DocJ2ME CLDC 1.145609Wed Feb 05 15:56:04 GMT 2003vm

EVMMethodInfo.java

/*
 *    EVMMethodInfo.java    1.6    01/04/27 SMI
 *
 * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package vm;
/*
 * VM-specific internal representation of
 * a method. Target-machine independent.
 *
 * See also EVMClass for VM-specific info associated with each class.
 * See also EVMVM for VM-specific info not associated directly with any class.
 */
import components.*;
import vm.VMMethodInfo;
import vm.Const;
import vm.EVMConst;
import util.DataFormatException;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Hashtable;
import java.util.StringTokenizer;

public class
EVMMethodInfo extends VMMethodInfo implements Const, EVMConst {
    private boolean        impureCode = false;
    private boolean        mustAlign  = false;
    private boolean        codeExamined = false;
    public  MethodInfo method;

    private String        myNativeName;

    private int inlining;
    static final int NO_INLINE_FLAG = (0x1 << 24);
    static final int SAME_CLASS_FLAG = (0x1 << 25);
    static final int REDO_INLINING_FLAG = (0x1 << 26);

    /** Keep this var to make code look the same as in inline.c */
    static final boolean UseLosslessQuickOpcodes = false;

    /**
     * Flag used in quickenCode() to save old methodblock info so we can
     * use it here for inlining.  This affects invokevirtual_quick which
     * typically overwrites the methodblock info with <methodtable offset>
     * and <nargs>.
     */
    public static final boolean SAVE_TARGET_METHODS = true;

    public EVMMethodInfo( MethodInfo m  ){
    method = m;
    method.vmMethodInfo = this;
    }

    private void examineCode( ) throws DataFormatException {
    impureCode = false;
    mustAlign  = false;
    if ( method.code == null ){
        codeExamined = true;
        return;
    }
    byte [] code = method.code;
    int    ncode  = code.length;
    int    opcode;
    for( int i = 0; i < ncode; i += method.opcodeLength(i)) {
        switch (opcode = (int)code[i]&0xff) {

        case opc_tableswitch:
        case opc_lookupswitch:
        mustAlign = true;
        break;

        case opc_ldc:
        case opc_ldc_w:
        case opc_ldc2_w:
        case opc_getstatic:
        case opc_putstatic:
        case opc_getfield:
        case opc_putfield:
        case opc_invokevirtual:
        case opc_invokespecial:
        case opc_invokestatic:
        case opc_invokeinterface:
        case opc_new:
        case opc_anewarray:
        case opc_checkcast:
        case opc_instanceof:
        case opc_multianewarray:
        impureCode = true; // all get quicked.
        break;
        }
    }
    codeExamined = true;
    }

    public boolean isCodePure() throws DataFormatException {
    if ( ! codeExamined ){
        examineCode(); // may throw exception without setting codeExamined
    }
    return !impureCode;
    }

    public int alignment() throws DataFormatException {
    if ( ! codeExamined ){
        examineCode(); // may throw exception without setting codeExamined
    }
    return mustAlign ? 4 : 1;
    }

    private static int methodNumber = 0;

    public String getNativeName() {
    if ( myNativeName == null ){
        // need to take parameter list into account.
        // OR just enumerate, as the actual name doesn't matter.
        // FIXME
        myNativeName = ((EVMClass)(method.parent.vmClass)).getNativeName()+ methodNumber;
        methodNumber+= 1;
    }
    return myNativeName;
    }

    public boolean
    hasBody(){
    return ( (method.access & (ACC_ABSTRACT|ACC_NATIVE) )== 0 );
    }

    public int
    EVMflags(){
    int flagval = 0;
    int a = method.access;
    if ( (a&ACC_PUBLIC) != 0 ) flagval |= EVM_METHOD_ACC_PUBLIC;
    if ( (a&ACC_PRIVATE) != 0 ) flagval |= EVM_METHOD_ACC_PRIVATE;
    if ( (a&ACC_PROTECTED) != 0 ) flagval |= EVM_METHOD_ACC_PROTECTED;
    if ( (a&ACC_STATIC) != 0 ) flagval |= EVM_METHOD_ACC_STATIC;
    if ( (a&ACC_FINAL) != 0 ) flagval |= EVM_METHOD_ACC_FINAL;
    if ( (a&ACC_SYNCHRONIZED) != 0 ) flagval |= EVM_METHOD_ACC_SYNCHRONIZED;
    if ( (a&ACC_NATIVE) != 0 ) flagval |= EVM_METHOD_ACC_NATIVE;
    if ( (a&ACC_ABSTRACT) != 0 ) flagval |= EVM_METHOD_ACC_ABSTRACT;
    return flagval;

    }

    public int methodOffset( ){
    int off = method.methodTableIndex;
    if ( off < 0 ){
        /*
         * off < 0 means that we do not have a methodtable or
         * imethodtable entry for this method. This is ok if it is:
         * - private, or
         * - static, or
         * - <init>
         * Otherwise, this is an error.
         */
        if ( method.isStaticMember( ) 
        || method.isPrivateMember( ) 
        || method.name.string.equals("<init>") ) {
            return 0;
        } else {
        throw new Error("Bad method offset for "+method.qualifiedName() );
        }
    }
    return off;
    }

    /** Attempt to inline the code of this method */
    final static int inline_NOT_DONE = 0;
    final static int inline_IN_PROGRESS = 1;
    final static int inline_DONE = 2;

    private int inlineState = inline_NOT_DONE;

    public void inlineCode() {
        new Error().printStackTrace();

        boolean isRewritten = false;
    if (inlineState == inline_NOT_DONE) { 
        inlineState = inline_IN_PROGRESS;
    } else { 
        return;
    }
    ConstantObject[] cp = method.parent.constants;
    byte[] code = method.code;
    byte[] rewrite;
    int tmi = 0;            // target method index

    for (int pc = 0; pc < code.length; ) {
        int opcode = code[pc] & 0xff;

        switch (opcode) {
                
        case opc_invokevirtual_fast:
        case opc_invokespecial_fast: 
        case opc_invokestatic_fast: {
        int index = method.getUnsignedShort(pc + 1);
        MethodConstant mc = (MethodConstant) cp[index];
        VMMethodInfo targetMethod = mc.find().vmMethodInfo;
        rewrite = MethodCallInline(pc, (EVMMethodInfo)targetMethod);
        if (rewrite != null) {
            isRewritten = true;
            System.arraycopy(rewrite, 0, code, pc, 3);
        }
        pc += 3;
        break;
        }

        default:
            pc += method.opcodeLength(pc);
        break;
        }
    }
    if (isRewritten) 
        compress();
    inlineState = inline_DONE;
    }

    /* This method is called to determine whether the method "mb" called at
     * from instruction "pc" can be inlined.  If not, the value null is
     * returned.  If so, an array of three bytes, which should overwrite the
     * method invocation, is returned
     */
    private byte[] MethodCallInline(int pc, EVMMethodInfo mb) {
    byte code[] = method.code;
    int opcode = code[pc] & 0xff;

    if (opcode == opc_invokevirtual_fast) {   
        /* This is a virtual method call.  No use even bother trying to
         * inline the method, unless its final
         */
        if (((mb.method.access & ACC_FINAL) == 0) 
           && ((mb.method.parent.access & ACC_FINAL) == 0))
        return null;
        }
            
    int mbInlining = mb.getInlining();
    if ((mbInlining & NO_INLINE_FLAG) != 0)
        return null;
    /* Does this inlining have a dependency on the constant pool, and so
     * can only be used on a method in the same class. */
    if ((mbInlining & SAME_CLASS_FLAG) != 0) {
        if (this.method.parent != mb.method.parent)
        return null;
    }
    /* There is a inlining.  Copy that value into "result" */
    byte[] result = new byte[3];
    result[0] = INLINING_WORD1(mbInlining);
    result[1] = INLINING_WORD2(mbInlining);
    result[2] = INLINING_WORD3(mbInlining);
    return result;
    }

    public int getInlining() { 
    MethodInfo mi = this.method;
    if (inlining == 0) { 
        if (   ((mi.access & (ACC_ABSTRACT | ACC_NATIVE |
                  ACC_SYNCHRONIZED)) != 0)
        || (mi.exceptionTable.length > 0)) {
        inlining = NO_INLINE_FLAG;
        } else { 
        inlineCode();
        inlining = calculateInlining();
        
        /*******
        if (inlining != NO_INLINE_FLAG) { 
            String sameClass = 
            ((inlining & SAME_CLASS_FLAG) != 0) ? "*" : "";
            System.out.print("get: " + this + " =>" + sameClass);
            System.out.println(" " + disassembleInlining());
        }
        ********/
        }
    }
    return inlining;
    }

    /* Given a method, determine if it can be "inlined" into three or fewer
     * bytes.
     */
    private int calculateInlining() { 
        MethodInfo mb = this.method;
    byte[] code = mb.code;

    /* The checkThis flag indicates that the resulting code must 
     * throw a NullPointerException if the first argument is null
     */
    boolean checkThis = ((mb.access & ACC_STATIC) == 0) 
                     && !method.name.string.equals("<init>");
        
    boolean redoInlining = false;
    int stackSize, stackBase;
    OpcodeInfoType opPtr;

    stackSize = 0;
    stackBase = 0;            // Prevent javac warning
    for (int pc = 0; ; pc++) { 
        /* At this point in our simulation of the execution of the
         * method, registers stackBase .. stackBase+stackSize - 1 are
         * pushed onto the the stack.  pc points to the next
         * instruction to look at.
         */
        int opcode = code[pc] & 0xff;
        int opcode2;
        int reg, regSize, nextReg;

        if (stackSize == 0)
        stackBase = 0;
        nextReg = stackBase + stackSize;
        opPtr = OpcodeInfo[opcode];

        switch (opPtr.opcode) { 

        case opc_iload_0: /* register load.  regnum from opcode */
        case opc_iload:    /* register load.  regnum from pc[1] */
            if (opPtr.opcode == opc_iload_0) { 
            reg = REGNUM(opPtr);
        } else { 
            reg = code[pc + 1] & 0xff; pc++;
        }
        regSize = REGSIZE(opPtr);
        if (stackSize == 0) /* stack is currently empty */
            stackBase = reg;
        else if (nextReg != reg)
            return NO_INLINE_FLAG;
        stackSize += regSize;
        continue;

        case opc_pop:    /* pop stack, or nop */
        stackSize -= REGSIZE(opPtr);
        continue;

        case opc_nonnull_quick: /* special instruction */
        if (nextReg == 1) {
            /* We're checking register 0 to ensure that it isn't null */
            stackSize = 0; checkThis = true;
            continue;
        }
        return NO_INLINE_FLAG;

        case opc_invokeignored_quick: /* special instruction */
        {        
        int popCount = code[pc + 1] & 0xff;
        if (code[pc + 2] != 0) {  
            /* We only know how to check register 0 for non-null ness */
            if (nextReg != popCount)
            return NO_INLINE_FLAG;
            checkThis = true;
            stackSize -= popCount;
        } else { 
            stackSize -= popCount;          
        }
        pc += 2;
        continue;
        }

        case opc_return:    /* return void or value */
        return makeReturnResult(checkThis, nextReg, REGSIZE(opPtr));

        case opc_iadd: {    /* any simple instruction */
        int ilength = opcLengths[opcode];
        int result;
        opcode2 = code[pc + ilength] & 0xff;

        if (!((opPtr.outStack > 0)
             ? isXreturn(opcode2)
             : (opcode2 == opc_return
                || opcode == opc_athrow)))
            return NO_INLINE_FLAG;

        if ((opPtr.flags & OpcodeInfoType.NULL_CHECK) != 0
            && (stackBase == 0)) {
            /* We don't need to generate code to check for null, since
             * the instruction already does it.
             */
            checkThis = false;
        }
        switch (ilength) {
        case 1:
            result =
            makeOpcodeResult(checkThis, nextReg, opPtr.inStack,
                     1, opcode, 0, 0);
            break;
        case 2:
            result = 
            makeOpcodeResult(checkThis, nextReg, opPtr.inStack,
                     2, opcode, code[pc+1] & 0xff, 0);
            break;
        case 3:
            result = 
            makeOpcodeResult(checkThis, nextReg, opPtr.inStack,
                     3, opcode,
                     code[pc+1] & 0xff, code[pc+2] & 0xff);
            break;
        default:
            throw new RuntimeException("sysAssert(FALSE);");
            // result = NO_INLINE_FLAG; // not reached
            // break;            // not reached
        }
        if ((result & NO_INLINE_FLAG) == 0) { 
            if ((opPtr.flags & OpcodeInfoType.CONSTANT_POOL) != 0) 
            result |= SAME_CLASS_FLAG;
            if (redoInlining) 
            result |= REDO_INLINING_FLAG;
        }
        return result;
        }

        default:
        throw new RuntimeException("sysAssert(FALSE);");
        case 255:        /* random instruction */
        return NO_INLINE_FLAG;

        } /* of switch statement */
    } /* end of for loop */
    }

    /* This method is called to create the code that is actually going to
     * replace the indicated method, when the method does nothing, or when
     * it simply returns one of its arguments.
     *
     * It takes the following arguments:
     *   mb:          Method we are examining
     *   checkThis:   If true,  We must specially check that the first argument
     *                "this" isn't null. 
     *   highReg,     One greater than the highest register on the stack when
     *                the return or Xreturn is called.
     *   returnSize   Size of the return (0 for return, 1 for ireturn, 
     *                 2 for lreturn, etc);
     *
     * We have to emulate the method call in 3 bytes.  At the time the
     * method is called, the arguments reg[0] . . . reg[mb->args_size - 1]
     * are pushed on the stack.
     */
    static int[] poppers = { opc_nop, opc_pop, opc_pop2 };
    private int makeReturnResult(boolean checkThis, int highReg, int returnSize) { 
        MethodInfo mb = method;
    int argsSize = mb.argsSize;
    if (returnSize == 0) { 
        /* Return void */
        return MAKE_INLINING(opc_invokeignored_quick, argsSize,
                 (checkThis ? 1 : 0));
    } else {
        /* Return some value from the stack */
        int returnReg = highReg - returnSize;  
        int excessArgs =  argsSize - returnSize - returnReg;
        // sysAssert(returnReg >= 0 && returnSize >= 0);
        if (returnReg == 0) { 
        /* Returning reg0 or reg0/reg1 */
        if (checkThis) { 
            /* Must be returning reg0, which is also checked. We
             * require argsSize >= 2 (which is the same thing as
             * excessArgs >= 1), because otherwise the "dup" might
             * overflow the stack.  More sophisticated inliners
             * might see if there is space on the caller's stack.
             */
            // sysAssert(returnSize == 1);
            if (argsSize < 2) { 
            return NO_INLINE_FLAG;
            } else if (excessArgs > 2) { 
                return NO_INLINE_FLAG;
            } else { 
                return MAKE_INLINING(poppers[excessArgs],
                         opc_dup,
                         opc_nonnull_quick);
            }
        } else {
            /* We're returning reg0 or reg0/reg1 which isn't null
             * checked.  We just pop extraneous stuff off the stack
             * */
            return MAKE_INLINING(opc_invokeignored_quick,
                     excessArgs, 0);
        }
        } else {
        /* At this point, returnReg > 0.  We're returning something
         * other than the bottom of the stack.
         */
            if (returnSize == 1 && returnReg == 1) { 
            if (excessArgs > 2) { 
            return NO_INLINE_FLAG;
            }
            return MAKE_INLINING(poppers[excessArgs], 
                     opc_swap, 
                     checkThis ? opc_nonnull_quick
                        : opc_pop);
        }
        return NO_INLINE_FLAG;
        }
    }
    }

    /* This method is called to create the code that is actually going to
     * replace the indicated method
     * 
     * makeOpcodeResult is used to create a inlining that can be used anywhere
     * It takes the following arguments:
     *
     *   mb:          Method we are examining
     *   checkThis:   If true,  We must specially check that the first argument
     *                "this" isn't null.  This condition is >>NOT<< tested by 
     *                the generated code.
     *   nextReg:     In the emulation, the highest register on the stack is
     *                reg[nextReg - 1].
     *   icount       The number of bytes of instructions that follow.
     *   opcode, op1, op2
     *                The bytes of instruction.
     *
     * We have to emulate the method call in 3 bytes.  At the time the
     * method is called, the arguments reg[0] . . . reg[mb->args_size - 1]
     * are pushed on the stack.  So in three bytes, we have to:
     *     Remove any excess arguments from the stack.
     *     Perform the operation on the indicated stack registers
     *     Remove any objects lower down on the stack (this is hard!)
     *     Make sure that reg[0] is checked for being non-null, if necessary.
     */ 
     private int makeOpcodeResult(boolean checkThis, 
                  int nextReg, int opcodeArgCount,
                  int icount, int opcode, int op1, int op2) {
        MethodInfo mb = method;
    int firstReg = (opcodeArgCount == 0) ? 0 : nextReg - opcodeArgCount;
    // sysAssert(firstReg >= 0 && opcodeArgCount >= 0 && icount > 0);

    if (firstReg > 0) {
        /* There are extra registers at the bottom of the stack */
        return makePoppingResult(checkThis, firstReg, opcodeArgCount, 
                     icount, opcode, op1, op2);
    } else {
        /* No extra registers at bottom of stack */
        int argsSize = mb.argsSize;
        int excessArgs = argsSize - opcodeArgCount; /* extra at top */
        int popSpace = 3 - icount; /* space to pop args at top */

        int result = 0;
        int i;

        if (checkThis) { 
        /* Unless this is a constant method that ignores all of its
         * arguments, we don't really have any way of checking
         * register 0 if the instructions doesn't.  If it is a
         * constant instruction, deduct one from both popSpace and
         * from excessArgs, since we are popping that last argument
         * when an opc_nonnull_quick;
         */
        if (opcodeArgCount > 0 || popSpace == 0)
            return NO_INLINE_FLAG;
        popSpace--; excessArgs--; 
        // sysAssert(excessArgs >= 0);
        }
        if (excessArgs > 2 * popSpace) 
        return NO_INLINE_FLAG;
        for (i = 0; i < popSpace; i++) { 
        /* If excessArgs <= popSpace, the following generates excessArgs
         * "pops" followed by nops.  Otherwise, it generates 
         * excessArgs - popSpace pop2's followed by pop's.
         */
        int opcodeTmp = (excessArgs <= i) ? opc_nop 
            : (excessArgs <= popSpace + i) ? opc_pop 
            : opc_pop2;
        result |= (opcodeTmp << (i << 3));
        }
        if (checkThis) 
        result |= opc_nonnull_quick << ((i++) << 3);
        // sysAssert(i + icount == 3);
        switch (icount) { 
           case 3: result |= op2 << ((i + 2) << 3);
           case 2: result |= op1 << ((i + 1) << 3);
           case 1: result |= opcode << ((i + 0) << 3);
        }
        return result;
    }
    }

    /* 
     * Called by makeOpcodeResult.  
     * Same arguments.  But there are extra arguments on the bottom to pop.
     */
    private int makePoppingResult(boolean checkThis, 
                  int firstReg, int opcodeArgCount,
                  int icount, int opcode, int op1, int op2) {
        MethodInfo mb = method;
    int argsSize = mb.argsSize;
    int excessArgs = argsSize - opcodeArgCount - firstReg; /* extra on top*/

    if (icount > 1)
        /* We're just not prepared to deal with this. */
        return NO_INLINE_FLAG;

    if (OpcodeInfo[opcode].outStack == 0) {
        int result = 0;
        /* Something like an array store, that leaves no value on the
               stack */
        int i = 0;
        /* We can't deal with checkThis, since it might reverse the order of
         * an exception.  We have a total of two instructions to do all the 
         * pre and post popping.
         */
        if (checkThis || ((excessArgs + 1)/2 + (firstReg + 1)/2) > 2)
        return NO_INLINE_FLAG;
        for (; excessArgs > 0; excessArgs -=2) /* pre popping */
        result |= (excessArgs == 1 ? opc_pop : opc_pop2)
            << ((i++) << 3);
        result |= opcode << ((i++) << 3);
        for (; firstReg > 0; firstReg -=2) /* post popping */
        result |= (firstReg == 1 ? opc_pop : opc_pop2)
            << ((i++) << 3);
        while (i < 3)
        result |= opc_nop << ((i++) << 3);
        return result;
    }

    if (excessArgs > 0 || firstReg > 1)
        /* We can't both do useful work and remove more than this many 
         * items from the stack. */
        return NO_INLINE_FLAG;

    if (opcodeArgCount == 1) { 
        return MAKE_INLINING(opc_swap, 
                 checkThis ? opc_nonnull_quick
                    : opc_pop, 
                 opcode);
    }
    if (   ((OpcodeInfo[opcode].flags &
         (OpcodeInfoType.NULL_CHECK | OpcodeInfoType.CAN_ERROR)) == 0)
        && (OpcodeInfo[opcode].outStack == 1)) {
        /* The result creates one thing on the stack, and it can't error */
        return MAKE_INLINING(opcode,
                 opc_swap,
                 checkThis ? opc_nonnull_quick
                    : opc_pop);
    }
    return NO_INLINE_FLAG;
    }

    private static boolean isXreturn(int opcode) {
    return (opcode >= opc_ireturn) && (opcode <= opc_areturn);
    }

    private static byte INLINING_WORD1(int simp) {
    return (byte) (simp & 0xFF);
    }

    private static byte INLINING_WORD2(int simp) {
    return (byte) ((simp >> 8) & 0xFF);
    }

    private static byte INLINING_WORD3(int simp) {
    return (byte) ((simp >> 16) & 0xFF);
    }

    private static int MAKE_INLINING(int op1, int op2, int op3) {
    return (op1 << 0) + (op2 << 8) + (op3 << 16);
    }

    private static int REGSIZE(OpcodeInfoType ptr) {
    return (ptr.inStack);
    }

    private static int REGNUM(OpcodeInfoType ptr) {
    return (ptr.outStack);
    }

    static OpcodeInfoType[] OpcodeInfo = {
    /*  { opc_pop  <number of words to pop from stack> } 
     *  { opc_iadd <words popped from stack> <words pushed to stack> }
     *  { opc_iload <words pushed to stack> } 
     *  { opc_iload_0 <words pushed to stack> <implicit register> } 
     *  { opc_return  <words returned> } 
     *    255 indicates opcode that we can't inline
     *  other values are special opcodes that must be handled specially
     */

    /* nop               */ new OpcodeInfoType(opc_pop, 0),
    /* aconst_null       */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* iconst_m1         */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* iconst_0          */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* iconst_1          */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* iconst_2          */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* iconst_3          */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* iconst_4          */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* iconst_5          */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* lconst_0          */ new OpcodeInfoType(opc_iadd, 0, 2),
    /* lconst_1          */ new OpcodeInfoType(opc_iadd, 0, 2),
    /* fconst_0          */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* fconst_1          */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* fconst_2          */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* dconst_0          */ new OpcodeInfoType(opc_iadd, 0, 2),
    /* dconst_1          */ new OpcodeInfoType(opc_iadd, 0, 2),
    /* bipush            */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* sipush            */ new OpcodeInfoType(opc_iadd, 0, 1),
    /* ldc               */ new OpcodeInfoType(255),
    /* ldc_w             */ new OpcodeInfoType(255),
    /* ldc2_w            */ new OpcodeInfoType(255),
    /* iload             */ new OpcodeInfoType(opc_iload, 1),
    /* lload             */ new OpcodeInfoType(opc_iload, 2),
    /* fload             */ new OpcodeInfoType(opc_iload, 1),
    /* dload             */ new OpcodeInfoType(opc_iload, 2),
    /* aload             */ new OpcodeInfoType(opc_iload, 1),
    /* iload_0           */ new OpcodeInfoType(opc_iload_0, 1, 0),
    /* iload_1           */ new OpcodeInfoType(opc_iload_0, 1, 1),
    /* iload_2           */ new OpcodeInfoType(opc_iload_0, 1, 2),
    /* iload_3           */ new OpcodeInfoType(opc_iload_0, 1, 3),
    /* lload_0           */ new OpcodeInfoType(opc_iload_0, 2, 0),
    /* lload_1           */ new OpcodeInfoType(opc_iload_0, 2, 1),
    /* lload_2           */ new OpcodeInfoType(opc_iload_0, 2, 2),
    /* lload_3           */ new OpcodeInfoType(opc_iload_0, 2, 3),
    /* fload_0           */ new OpcodeInfoType(opc_iload_0, 1, 0),
    /* fload_1           */ new OpcodeInfoType(opc_iload_0, 1, 1),
    /* fload_2           */ new OpcodeInfoType(opc_iload_0, 1, 2),
    /* fload_3           */ new OpcodeInfoType(opc_iload_0, 1, 3),
    /* dload_0           */ new OpcodeInfoType(opc_iload_0, 2, 0),
    /* dload_1           */ new OpcodeInfoType(opc_iload_0, 2, 1),
    /* dload_2           */ new OpcodeInfoType(opc_iload_0, 2, 2),
    /* dload_3           */ new OpcodeInfoType(opc_iload_0, 2, 3),
    /* aload_0           */ new OpcodeInfoType(opc_iload_0, 1, 0),
    /* aload_1           */ new OpcodeInfoType(opc_iload_0, 1, 1),
    /* aload_2           */ new OpcodeInfoType(opc_iload_0, 1, 2),
    /* aload_3           */ new OpcodeInfoType(opc_iload_0, 1, 3),
    /* iaload            */ new OpcodeInfoType(opc_iadd, 2, 1,
                           OpcodeInfoType.NULL_CHECK),
    /* laload            */ new OpcodeInfoType(opc_iadd, 2, 2,
                           OpcodeInfoType.NULL_CHECK),
    /* faload            */ new OpcodeInfoType(opc_iadd, 2, 1,
                           OpcodeInfoType.NULL_CHECK),
    /* daload            */ new OpcodeInfoType(opc_iadd, 2, 2,
                           OpcodeInfoType.NULL_CHECK),
    /* aaload            */ new OpcodeInfoType(opc_iadd, 2, 1,
                           OpcodeInfoType.NULL_CHECK),
    /* baload            */ new OpcodeInfoType(opc_iadd, 2, 1,
                           OpcodeInfoType.NULL_CHECK),
    /* caload            */ new OpcodeInfoType(opc_iadd, 2, 1,
                           OpcodeInfoType.NULL_CHECK),
    /* saload            */ new OpcodeInfoType(opc_iadd, 2, 1,
                           OpcodeInfoType.NULL_CHECK),
    /* istore            */ new OpcodeInfoType(255),
    /* lstore            */ new OpcodeInfoType(255),
    /* fstore            */ new OpcodeInfoType(255),
    /* dstore            */ new OpcodeInfoType(255),
    /* astore            */ new OpcodeInfoType(255),
    /* istore_0          */ new OpcodeInfoType(255),
    /* istore_1          */ new OpcodeInfoType(255),
    /* istore_2          */ new OpcodeInfoType(255),
    /* istore_3          */ new OpcodeInfoType(255),
    /* lstore_0          */ new OpcodeInfoType(255),
    /* lstore_1          */ new OpcodeInfoType(255),
    /* lstore_2          */ new OpcodeInfoType(255),
    /* lstore_3          */ new OpcodeInfoType(255),
    /* fstore_0          */ new OpcodeInfoType(255),
    /* fstore_1          */ new OpcodeInfoType(255),
    /* fstore_2          */ new OpcodeInfoType(255),
    /* fstore_3          */ new OpcodeInfoType(255),
    /* dstore_0          */ new OpcodeInfoType(255),
    /* dstore_1          */ new OpcodeInfoType(255),
    /* dstore_2          */ new OpcodeInfoType(255),
    /* dstore_3          */ new OpcodeInfoType(255),
    /* astore_0          */ new OpcodeInfoType(255),
    /* astore_1          */ new OpcodeInfoType(255),
    /* astore_2          */ new OpcodeInfoType(255),
    /* astore_3          */ new OpcodeInfoType(255),
    /* iastore           */ new OpcodeInfoType(opc_iadd, 3, 0,
                           OpcodeInfoType.NULL_CHECK),
    /* lastore           */ new OpcodeInfoType(opc_iadd, 4, 0,
                           OpcodeInfoType.NULL_CHECK),
    /* fastore           */ new OpcodeInfoType(opc_iadd, 3, 0,
                           OpcodeInfoType.NULL_CHECK),
    /* dastore           */ new OpcodeInfoType(opc_iadd, 4, 0,
                           OpcodeInfoType.NULL_CHECK),
    /* aastore           */ new OpcodeInfoType(opc_iadd, 3, 0,
                           OpcodeInfoType.NULL_CHECK),
    /* bastore           */ new OpcodeInfoType(opc_iadd, 3, 0,
                           OpcodeInfoType.NULL_CHECK),
    /* castore           */ new OpcodeInfoType(opc_iadd, 3, 0,
                           OpcodeInfoType.NULL_CHECK),
    /* sastore           */ new OpcodeInfoType(opc_iadd, 3, 0,
                           OpcodeInfoType.NULL_CHECK),
    /* pop               */ new OpcodeInfoType(opc_pop, 1),
    /* pop2              */ new OpcodeInfoType(opc_pop, 2),
    /* dup               */ new OpcodeInfoType(255),
    /* dup_x1            */ new OpcodeInfoType(255),
    /* dup_x2            */ new OpcodeInfoType(255),
    /* dup2              */ new OpcodeInfoType(255),
    /* dup2_x1           */ new OpcodeInfoType(255),
    /* dup2_x2           */ new OpcodeInfoType(255),
    /* swap              */ new OpcodeInfoType(255),
    /* iadd              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* ladd              */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* fadd              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* dadd              */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* isub              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* lsub              */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* fsub              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* dsub              */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* imul              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* lmul              */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* fmul              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* dmul              */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* idiv              */ new OpcodeInfoType(opc_iadd, 2, 1,
                           OpcodeInfoType.CAN_ERROR),
    /* ldiv              */ new OpcodeInfoType(opc_iadd, 4, 2,
                           OpcodeInfoType.CAN_ERROR),
    /* fdiv              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* ddiv              */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* irem              */ new OpcodeInfoType(opc_iadd, 2, 1,
                           OpcodeInfoType.CAN_ERROR),
    /* lrem              */ new OpcodeInfoType(opc_iadd, 4, 2,
                           OpcodeInfoType.CAN_ERROR),
    /* frem              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* drem              */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* ineg              */ new OpcodeInfoType(opc_iadd, 1, 1),
    /* lneg              */ new OpcodeInfoType(opc_iadd, 2, 2),
    /* fneg              */ new OpcodeInfoType(opc_iadd, 1, 1),
    /* dneg              */ new OpcodeInfoType(opc_iadd, 2, 2),
    /* ishl              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* lshl              */ new OpcodeInfoType(opc_iadd, 3, 2),
    /* ishr              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* lshr              */ new OpcodeInfoType(opc_iadd, 3, 2),
    /* iushr             */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* lushr             */ new OpcodeInfoType(opc_iadd, 3, 2),
    /* iand              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* land              */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* ior               */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* lor               */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* ixor              */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* lxor              */ new OpcodeInfoType(opc_iadd, 4, 2),
    /* iinc              */ new OpcodeInfoType(255),
    /* i2l               */ new OpcodeInfoType(opc_iadd, 1, 2),
    /* i2f               */ new OpcodeInfoType(opc_iadd, 1, 1),
    /* i2d               */ new OpcodeInfoType(opc_iadd, 1, 2),
    /* l2i               */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* l2f               */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* l2d               */ new OpcodeInfoType(opc_iadd, 2, 2),
    /* f2i               */ new OpcodeInfoType(opc_iadd, 1, 1),
    /* f2l               */ new OpcodeInfoType(opc_iadd, 1, 2),
    /* f2d               */ new OpcodeInfoType(opc_iadd, 1, 2),
    /* d2i               */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* d2l               */ new OpcodeInfoType(opc_iadd, 2, 2),
    /* d2f               */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* i2b               */ new OpcodeInfoType(opc_iadd, 1, 1),
    /* i2c               */ new OpcodeInfoType(opc_iadd, 1, 1),
    /* i2s               */ new OpcodeInfoType(opc_iadd, 1, 1),
    /* lcmp              */ new OpcodeInfoType(opc_iadd, 4, 1),
    /* fcmpl             */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* fcmpg             */ new OpcodeInfoType(opc_iadd, 2, 1),
    /* dcmpl             */ new OpcodeInfoType(opc_iadd, 4, 1),
    /* dcmpg             */ new OpcodeInfoType(opc_iadd, 4, 1),
    /* ifeq              */ new OpcodeInfoType(255),
    /* ifne              */ new OpcodeInfoType(255),
    /* iflt              */ new OpcodeInfoType(255),
    /* ifge              */ new OpcodeInfoType(255),
    /* ifgt              */ new OpcodeInfoType(255),
    /* ifle              */ new OpcodeInfoType(255),
    /* if_icmpeq         */ new OpcodeInfoType(255),
    /* if_icmpne         */ new OpcodeInfoType(255),
    /* if_icmplt         */ new OpcodeInfoType(255),
    /* if_icmpge         */ new OpcodeInfoType(255),
    /* if_icmpgt         */ new OpcodeInfoType(255),
    /* if_icmple         */ new OpcodeInfoType(255),
    /* if_acmpeq         */ new OpcodeInfoType(255),
    /* if_acmpne         */ new OpcodeInfoType(255),
    /* goto              */ new OpcodeInfoType(255),
    /* jsr               */ new OpcodeInfoType(255),
    /* ret               */ new OpcodeInfoType(255),
    /* tableswitch       */ new OpcodeInfoType(255),
    /* lookupswitch      */ new OpcodeInfoType(255),
    /* ireturn           */ new OpcodeInfoType(opc_return, 1),
    /* lreturn           */ new OpcodeInfoType(opc_return, 2),
    /* freturn           */ new OpcodeInfoType(opc_return, 1),
    /* dreturn           */ new OpcodeInfoType(opc_return, 2),
    /* areturn           */ new OpcodeInfoType(opc_return, 1),
    /* return            */ new OpcodeInfoType(opc_return, 0),
    /* getstatic         */ new OpcodeInfoType(255),
    /* putstatic         */ new OpcodeInfoType(255),
    /* getfield          */ new OpcodeInfoType(255),
    /* putfield          */ new OpcodeInfoType(255),
    /* invokevirtual     */ new OpcodeInfoType(255),
    /* invokespecial     */ new OpcodeInfoType(255),
    /* invokestatic      */ new OpcodeInfoType(255),
    /* invokeinterface   */ new OpcodeInfoType(255),
    /* xxxunusedxxx      */ new OpcodeInfoType(255),
    /* new               */ new OpcodeInfoType(255),
    /* newarray          */ new OpcodeInfoType(opc_iadd, 1, 1,
                           OpcodeInfoType.CAN_ERROR),
    /* anewarray         */ new OpcodeInfoType(255),
    /* arraylength       */ new OpcodeInfoType(opc_iadd, 1, 1,
                           OpcodeInfoType.NULL_CHECK),
    /* athrow            */ new OpcodeInfoType(opc_iadd, 1, 0,
                           OpcodeInfoType.NULL_CHECK
                           | OpcodeInfoType.CAN_ERROR),
    /* checkcast         */ new OpcodeInfoType(255),
    /* instanceof        */ new OpcodeInfoType(255),
    /* monitorenter      */ new OpcodeInfoType(opc_iadd, 1, 0,
                           OpcodeInfoType.NULL_CHECK
                           | OpcodeInfoType.CAN_ERROR),
    /* monitorexit       */ new OpcodeInfoType(opc_iadd, 1, 0,
                           OpcodeInfoType.NULL_CHECK
                           | OpcodeInfoType.CAN_ERROR),
    /* wide              */ new OpcodeInfoType(255),
    /* multianewarray    */ new OpcodeInfoType(255),
    /* ifnull            */ new OpcodeInfoType(255),
    /* ifnonnull         */ new OpcodeInfoType(255),
    /* goto_w            */ new OpcodeInfoType(255),
    /* jsr_w             */ new OpcodeInfoType(255),
    /* breakpoint        */ new OpcodeInfoType(255)

        /* XXXX   FY:  I still need to add more info here */

    };

    /**
     * Print the code as Java assembly language instructions
     */  
    String disassembleInlining() {
    byte codeBytes[] = new byte[3];
    // Copy inlining into codeBytes[] buffer
    codeBytes[0] = (byte)(inlining & 0xff);
    codeBytes[1] = (byte)((inlining >> 8) & 0xff);
    codeBytes[2] = (byte)((inlining >> 16) & 0xff);
    return MethodInfo.disassemble(codeBytes, 0, 3);
    } 

    private String myName;
    public String toString() {
    if (myName == null) {
        myName = method.parent.className + "." + method.name.string + 
        method.type.string;
    } 
    return myName;
    }

    static int total = 0;

    // After inlining some code, we try to see if we can remove code.  For
    // example, the frequent case of opc_aload_0 invokeingored_quick #1 T can
    // simply "go away"

    final boolean compress () {
    MethodInfo mb = method;
        boolean rewritten = false;
        byte[] code = mb.code;
    int[] stack = new int[mb.stack + 1];
    int stackHeight = 0;
    int nextpc;
    java.util.BitSet targets = mb.getLabelTargets();

    for (int pc = 0; pc < code.length; pc = nextpc) {
        nextpc = pc + mb.opcodeLength(pc);
        int opcode = code[pc] & 0xff;
        int popping = 0;
        boolean checkThis = false;

        if (targets.get(pc)) { 
            stackHeight = 0;
        }
        stack[stackHeight] = pc;

        // Invariant.  the stackheight at this point is stackHeight or less.
        // 
        // We can pop n items from the stack (where n <= stackHeight) by
        // simply deleting all the code from stackHeight[n] to this point
        // in the code.  No side effects are removed.
        // 
        // Note that instructions that have a side effect should set
        // stackHeight = 0, to indicate that it can't be deleted.

        switch (opcode) {
        case opc_nop:
        case opc_ineg: case opc_fneg:
        case opc_i2f: case opc_f2i:
        case opc_i2b: case opc_i2c: case opc_i2s:
        case opc_newarray:
        case opc_anewarray_fast:
        case opc_instanceof_fast:
        case opc_lneg: case opc_dneg:
        case opc_l2d: case opc_d2l:
            // these don't change stack height, and we know as much about
            // the stack before as we do after.
            break;

        case opc_aconst_null: 
        case opc_iconst_m1: case opc_iconst_0: 
        case opc_iconst_1:  case opc_iconst_2:  
        case opc_iconst_3:  case opc_iconst_4:
        case opc_iconst_5:  
        case opc_fconst_0:  case opc_fconst_1: 
        case opc_fconst_2:
        case opc_bipush:    case opc_sipush:
        case opc_iload:     case opc_fload:    
        case opc_aload:
        case opc_iload_0:   case opc_iload_1:  
        case opc_iload_2:   case opc_iload_3:
        case opc_fload_0:   case opc_fload_1:  
        case opc_fload_2:   case opc_fload_3:
        case opc_aload_0:   case opc_aload_1:
        case opc_aload_2:   case opc_aload_3:
        case opc_getstatic_fast:
        case opc_dup:    
            // These push some value onto the stack, no matter what was
            // there before
            stackHeight += 1;
        break;

        case opc_lconst_0: case opc_lconst_1:
        case opc_dconst_0: case opc_dconst_1:
        case opc_lload:    case opc_dload:
        case opc_lload_0:  case opc_lload_1:
        case opc_lload_2:  case opc_lload_3:
        case opc_dload_0:  case opc_dload_1:
        case opc_dload_2:  case opc_dload_3:
        case opc_getstatic2_fast:
            // These push two values onto the stack, no matter what was 
            // there before.
            stackHeight += 2;
        break;

        case opc_i2l: case opc_i2d:
        case opc_f2l: case opc_f2d:
            // if we knew the top element of the stack, we know more
            stackHeight = (stackHeight < 1) ? 0 : stackHeight + 1;
        break;

        case opc_iadd: case opc_fadd:
        case opc_isub: case opc_fsub:
        case opc_imul: case opc_fmul:
        case opc_fdiv: case opc_frem:
        case opc_ishl: case opc_ishr: case opc_iushr:
        case opc_iand: case opc_ior: case opc_ixor:
        case opc_l2i: case opc_l2f:
        case opc_d2i:  case opc_d2f:
        case opc_fcmpl: case opc_fcmpg:
            // if we knew the top two elements of the stack, the stack
            // has just shrunk
            stackHeight = (stackHeight < 2) ? 0 : stackHeight - 1;
        break;

        case opc_lshl: case opc_lshr: case opc_lushr:
            // if we knew the top three elements of the stack, we now
            // know the top two
            stackHeight = (stackHeight < 3) ? 0 : stackHeight  - 1;
        break;

        case opc_lcmp: case opc_dcmpl: case opc_dcmpg:
            // if we knew the top 4 elements of the stack, we now
            // know the top element
            stackHeight = (stackHeight < 4) ? 0 : stackHeight - 3;
        break;
          
        case opc_ladd: case opc_dadd:
        case opc_lsub: case opc_dsub:
        case opc_lmul: case opc_dmul:
        case opc_ddiv: case opc_drem:
        case opc_land: case opc_lor: case opc_lxor:
            // if we knew the top 4 elements of the stack, we now
            // know the top 2
            stackHeight = (stackHeight < 4) ? 0 : stackHeight - 2;
        break;

        // The dup's (other than opc_dup) deal with the stack in 
        // a way that's not worth the hassle of dealing with.

        case opc_getfield_fast:
        case opc_arraylength:
            // If we throw away the result, then we just need to check that
            // the value is non-null.
        if (code[nextpc] == (byte)(opc_pop)) { 
            checkThis = true;
            nextpc += 1;
        } else { 
            stackHeight = 0;
        }
        break;

        case opc_pop2:
        popping++;    // fall thru
        case opc_pop:
            // We have to be careful.  The inliner may produce code that
            // does correspond to the stack.  For example, it might 
            // produce "pop pop2" to remove a double then an int.  We need
            // to deal with series of them at once.
        if (stackHeight > 0) {
            popping++;
            for(;;) {
            opcode = code[++pc] & 0xFF;
            if (opcode == opc_pop) 
                popping++;
            else if (opcode == opc_pop2)
                popping += 2;
            else 
                break;
            }
            nextpc = pc;
        }
        break;

        case opc_invokeignored_quick: 
            popping = code[pc + 1] & 0xff;
        if (code[pc + 2] != 0) { 
            checkThis = true; popping--;
        } 
        break;
        
        default:
            stackHeight = 0;
        }
        
        if (checkThis || (popping > 0 && stackHeight > 0)) {
        rewritten = true;
            if (stackHeight >= popping) { 
            stackHeight -= popping;
            popping = 0;
        } else { 
            popping -= stackHeight;
            stackHeight = 0;
        }
        int start = stack[stackHeight];
            
        if (checkThis) { 
            if (popping == 0 && (nextpc - start != 3)) {
                mb.replaceCode(start, nextpc, opc_nonnull_quick);
            } else { 
            mb.replaceCode(start, nextpc, 
                       opc_invokeignored_quick, popping+1, 1);
            }
            stackHeight = 0;
        } else { 
            switch (popping) { 
            case 0:  
                mb.replaceCode(start, nextpc); break;
            case 1:  
                mb.replaceCode(start, nextpc, opc_pop); break; 
            case 2:  
                mb.replaceCode(start, nextpc, opc_pop2); break; 
            default: 
                mb.replaceCode(start, nextpc, opc_invokeignored_quick, 
                       popping, 0); 
            break;
            }
        }
        }
    }
    return rewritten;
    }
}

/**
 * Class used for inlining info.  See inlining code in MethodInfo
 */
class OpcodeInfoType { 
    int opcode;                // really the opcode type
    int inStack;
    int outStack;

    static final int CAN_ERROR = 0x01;    /* can give error in addition to
                       NULL_CHECK */
    static final int NULL_CHECK = 0x02;    /* checks that first arg isn't null */
    static final int CONSTANT_POOL = 0x04; /* uses the constant pool */
    int flags;

    OpcodeInfoType(int opcode, int inStack, int outStack, int flags) {
    this.opcode = opcode;
    this.inStack = inStack;
    this.outStack = outStack;
    this.flags = flags;
    }

    OpcodeInfoType(int opcode, int inStack, int outStack) {
    this(opcode, inStack, outStack, 0);
    }

    OpcodeInfoType(int opcode, int inStack) {
    this(opcode, inStack, 0, 0);
    }

    OpcodeInfoType(int opcode) {
    this(opcode, 0, 0, 0);
    }
};