EVMMethodInfopublic class EVMMethodInfo extends vm.VMMethodInfo implements jcc.EVMConst, jcc.Const
Fields Summary |
---|
private boolean | impureCode | private boolean | mustAlign | private boolean | codeExamined | public MethodInfo | method | private String | myNativeName | private int | inlining | static final int | NO_INLINE_FLAG | static final int | SAME_CLASS_FLAG | static final int | REDO_INLINING_FLAG | static final boolean | UseLosslessQuickOpcodesKeep this var to make code look the same as in inline.c | public static final boolean | SAVE_TARGET_METHODSFlag 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
and . | private static int | methodNumber | static final int | inline_NOT_DONEAttempt to inline the code of this method | static final int | inline_IN_PROGRESS | static final int | inline_DONE | private int | inlineState | static int[] | poppers | static OpcodeInfoType[] | OpcodeInfo | private String | myName | static int | total |
Constructors Summary |
---|
public EVMMethodInfo(MethodInfo m)
method = m;
method.vmMethodInfo = this;
|
Methods Summary |
---|
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;
| 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 byte[] | MethodCallInline(int pc, vm.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;
| private static int | REGNUM(vm.OpcodeInfoType ptr)
return (ptr.outStack);
| private static int | REGSIZE(vm.OpcodeInfoType ptr)
return (ptr.inStack);
| public int | alignment()
if ( ! codeExamined ){
examineCode(); // may throw exception without setting codeExamined
}
return mustAlign ? 4 : 1;
| 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 */
| final boolean | compress()
// 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"
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;
| java.lang.String | disassembleInlining()Print the code as Java assembly language instructions
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 void | examineCode()
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 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;
| public java.lang.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 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;
| public boolean | isCodePure()
if ( ! codeExamined ){
examineCode(); // may throw exception without setting codeExamined
}
return !impureCode;
| private static boolean | isXreturn(int opcode)
return (opcode >= opc_ireturn) && (opcode <= opc_areturn);
| 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;
}
| 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 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;
}
}
| 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;
| public java.lang.String | toString()
if (myName == null) {
myName = method.parent.className + "." + method.name.string +
method.type.string;
}
return myName;
|
|