/* * * * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ 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 jcc.Const; import jcc.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. // IMPL_NOTE: consider whether this should be fixed. 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) /* IMPL_NOTE: 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); } };