void dvmCompilerInlineMIR(CompilationUnit *cUnit) { int i; bool isRange = false; /* * Analyze the basic block containing an invoke to see if it can be inlined */ for (i = 0; i < cUnit->numBlocks; i++) { BasicBlock *bb = cUnit->blockList[i]; if (bb->blockType != kDalvikByteCode) continue; MIR *lastMIRInsn = bb->lastMIRInsn; int opCode = lastMIRInsn->dalvikInsn.opCode; int flags = dexGetInstrFlags(gDvm.instrFlags, opCode); /* No invoke - continue */ if ((flags & kInstrInvoke) == 0) continue; /* Not a real invoke - continue */ if (opCode == OP_INVOKE_DIRECT_EMPTY) continue; /* * If the invoke itself is selected for single stepping, don't bother * to inline it. */ if (SINGLE_STEP_OP(opCode)) continue; const Method *calleeMethod; switch (opCode) { case OP_INVOKE_SUPER: case OP_INVOKE_DIRECT: case OP_INVOKE_STATIC: case OP_INVOKE_SUPER_QUICK: calleeMethod = lastMIRInsn->meta.callsiteInfo->method; break; case OP_INVOKE_SUPER_RANGE: case OP_INVOKE_DIRECT_RANGE: case OP_INVOKE_STATIC_RANGE: case OP_INVOKE_SUPER_QUICK_RANGE: isRange = true; calleeMethod = lastMIRInsn->meta.callsiteInfo->method; break; default: calleeMethod = NULL; break; } if (calleeMethod) { tryInlineSingletonCallsite(cUnit, calleeMethod, lastMIRInsn, bb, isRange); return; } switch (opCode) { case OP_INVOKE_VIRTUAL: case OP_INVOKE_VIRTUAL_QUICK: case OP_INVOKE_INTERFACE: isRange = false; calleeMethod = lastMIRInsn->meta.callsiteInfo->method; break; case OP_INVOKE_VIRTUAL_RANGE: case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_INTERFACE_RANGE: isRange = true; calleeMethod = lastMIRInsn->meta.callsiteInfo->method; break; default: break; } if (calleeMethod) { tryInlineVirtualCallsite(cUnit, calleeMethod, lastMIRInsn, bb, isRange); return; } } }
/* * Perform static verification on instructions. * * As a side effect, this sets the "branch target" flags in InsnFlags. * * "(CF)" items are handled during code-flow analysis. * * v3 4.10.1 * - target of each jump and branch instruction must be valid * - targets of switch statements must be valid * - (CF) operands referencing constant pool entries must be valid * - (CF) operands of getfield, putfield, getstatic, putstatic must be valid * - (new) verify operands of "quick" field ops * - (CF) operands of method invocation instructions must be valid * - (new) verify operands of "quick" method invoke ops * - (CF) only invoke-direct can call a method starting with '<' * - (CF) <clinit> must never be called explicitly * - (CF) operands of instanceof, checkcast, new (and variants) must be valid * - new-array[-type] limited to 255 dimensions * - can't use "new" on an array class * - (?) limit dimensions in multi-array creation * - (CF) local variable load/store register values must be in valid range * * v3 4.11.1.2 * - branches must be within the bounds of the code array * - targets of all control-flow instructions are the start of an instruction * - (CF) register accesses fall within range of allocated registers * - (N/A) access to constant pool must be of appropriate type * - (CF) code does not end in the middle of an instruction * - (CF) execution cannot fall off the end of the code * - (earlier) for each exception handler, the "try" area must begin and * end at the start of an instruction (end can be at the end of the code) * - (earlier) for each exception handler, the handler must start at a valid * instruction * * TODO: move some of the "CF" items in here for better performance (the * code-flow analysis sometimes has to process the same instruction several * times). */ static bool verifyInstructions(const Method* meth, InsnFlags* insnFlags, int verifyFlags) { const int insnCount = dvmGetMethodInsnsSize(meth); const u2* insns = meth->insns; int i; /* the start of the method is a "branch target" */ dvmInsnSetBranchTarget(insnFlags, 0, true); for (i = 0; i < insnCount; /**/) { /* * These types of instructions can be GC points. To support precise * GC, all such instructions must export the PC in the interpreter, * or the GC won't be able to identify the current PC for the thread. */ static const int gcMask = kInstrCanBranch | kInstrCanSwitch | kInstrCanThrow | kInstrCanReturn; int width = dvmInsnGetWidth(insnFlags, i); OpCode opcode = *insns & 0xff; InstructionFlags opFlags = dexGetInstrFlags(gDvm.instrFlags, opcode); int offset, absOffset; if ((opFlags & gcMask) != 0) { /* * This instruction is probably a GC point. Branch instructions * only qualify if they go backward, so we need to check the * offset. */ int offset = -1; bool unused; if (dvmGetBranchTarget(meth, insnFlags, i, &offset, &unused)) { if (offset < 0) { dvmInsnSetGcPoint(insnFlags, i, true); } } else { /* not a branch target */ dvmInsnSetGcPoint(insnFlags, i, true); } } switch (opcode) { case OP_NOP: /* plain no-op or switch table data; nothing to do here */ break; case OP_CONST_STRING: case OP_CONST_STRING_JUMBO: if (!checkStringIndex(meth, i)) return false; break; case OP_CONST_CLASS: case OP_CHECK_CAST: if (!checkTypeIndex(meth, i, true)) return false; break; case OP_INSTANCE_OF: if (!checkTypeIndex(meth, i, false)) return false; break; case OP_PACKED_SWITCH: case OP_SPARSE_SWITCH: /* verify the associated table */ if (!dvmCheckSwitchTargets(meth, insnFlags, i)) return false; break; case OP_FILL_ARRAY_DATA: /* verify the associated table */ if (!checkArrayData(meth, i)) return false; break; case OP_GOTO: case OP_GOTO_16: case OP_IF_EQ: case OP_IF_NE: case OP_IF_LT: case OP_IF_GE: case OP_IF_GT: case OP_IF_LE: case OP_IF_EQZ: case OP_IF_NEZ: case OP_IF_LTZ: case OP_IF_GEZ: case OP_IF_GTZ: case OP_IF_LEZ: /* check the destination */ if (!dvmCheckBranchTarget(meth, insnFlags, i, false)) return false; break; case OP_GOTO_32: /* check the destination; self-branch is okay */ if (!dvmCheckBranchTarget(meth, insnFlags, i, true)) return false; break; case OP_NEW_INSTANCE: if (!checkNewInstance(meth, i)) return false; break; case OP_NEW_ARRAY: if (!checkNewArray(meth, i)) return false; break; case OP_FILLED_NEW_ARRAY: if (!checkTypeIndex(meth, i, true)) return false; break; case OP_FILLED_NEW_ARRAY_RANGE: if (!checkTypeIndex(meth, i, true)) return false; break; case OP_IGET: case OP_IGET_WIDE: case OP_IGET_OBJECT: case OP_IGET_BOOLEAN: case OP_IGET_BYTE: case OP_IGET_CHAR: case OP_IGET_SHORT: case OP_IPUT: case OP_IPUT_WIDE: case OP_IPUT_OBJECT: case OP_IPUT_BOOLEAN: case OP_IPUT_BYTE: case OP_IPUT_CHAR: case OP_IPUT_SHORT: /* check the field index */ if (!checkFieldIndex(meth, i, false)) return false; break; case OP_SGET: case OP_SGET_WIDE: case OP_SGET_OBJECT: case OP_SGET_BOOLEAN: case OP_SGET_BYTE: case OP_SGET_CHAR: case OP_SGET_SHORT: case OP_SPUT: case OP_SPUT_WIDE: case OP_SPUT_OBJECT: case OP_SPUT_BOOLEAN: case OP_SPUT_BYTE: case OP_SPUT_CHAR: case OP_SPUT_SHORT: /* check the field index */ if (!checkFieldIndex(meth, i, true)) return false; break; case OP_INVOKE_VIRTUAL: case OP_INVOKE_SUPER: case OP_INVOKE_DIRECT: case OP_INVOKE_STATIC: case OP_INVOKE_INTERFACE: case OP_INVOKE_VIRTUAL_RANGE: case OP_INVOKE_SUPER_RANGE: case OP_INVOKE_DIRECT_RANGE: case OP_INVOKE_STATIC_RANGE: case OP_INVOKE_INTERFACE_RANGE: /* check the method index */ if (!checkMethodIndex(meth, i)) return false; break; case OP_EXECUTE_INLINE: case OP_INVOKE_DIRECT_EMPTY: case OP_IGET_QUICK: case OP_IGET_WIDE_QUICK: case OP_IGET_OBJECT_QUICK: case OP_IPUT_QUICK: case OP_IPUT_WIDE_QUICK: case OP_IPUT_OBJECT_QUICK: case OP_INVOKE_VIRTUAL_QUICK: case OP_INVOKE_VIRTUAL_QUICK_RANGE: case OP_INVOKE_SUPER_QUICK: case OP_INVOKE_SUPER_QUICK_RANGE: if ((verifyFlags & VERIFY_ALLOW_OPT_INSTRS) == 0) { LOG_VFY("VFY: not expecting optimized instructions\n"); return false; } break; default: /* nothing to do */ break; } assert(width > 0); i += width; insns += width; } /* make sure the last instruction ends at the end of the insn area */ if (i != insnCount) { LOG_VFY_METH(meth, "VFY: code did not end when expected (end at %d, count %d)\n", i, insnCount); return false; } return true; }