OSDN Git Service

Move some verifier stuff around.
authorAndy McFadden <fadden@android.com>
Thu, 4 Nov 2010 23:31:37 +0000 (16:31 -0700)
committerAndy McFadden <fadden@android.com>
Fri, 5 Nov 2010 23:21:57 +0000 (16:21 -0700)
Once upon a time we started down the road of "just in time" register
map generation, which was intended to save flash and RAM storage by
generating register maps for methods as the GC needed them (rather than
doing them all up front, or on first load of a class).  This required
a lot of duplication of code, and in the end just wasn't worth it.

This removes the (#ifdefed-out) partial implementation.  Some functions
that were moved into VerifySubs to be shared with RegisterMap.c are
moved back to their previous homes.

Some temporary VERIFIER_STATS stuff is also getting checked in (#ifdefed
out).

Added an assert, tweaked a couple of comments.  No changes in behavior
are expected.

Change-Id: I20b915992add9906cb3638ac9432c910443a9a47

vm/Globals.h
vm/analysis/CodeVerify.c
vm/analysis/DexPrepare.c
vm/analysis/DexVerify.c
vm/analysis/DexVerify.h
vm/analysis/RegisterMap.c
vm/analysis/VerifySubs.c

index bb3995c..eaa9000 100644 (file)
@@ -658,6 +658,10 @@ struct DvmGlobals {
 
     /* some RegisterMap statistics, useful during development */
     void*       registerMapStats;
+
+#ifdef VERIFIER_STATS
+    VerifierStats verifierStats;
+#endif
 };
 
 extern struct DvmGlobals gDvm;
index b76ce75..49c1a3e 100644 (file)
@@ -357,6 +357,32 @@ static RegType primitiveTypeToRegType(PrimitiveType primType)
 }
 
 /*
+ * Given a 32-bit constant, return the most-restricted RegType enum entry
+ * that can hold the value.
+ */
+static char determineCat1Const(s4 value)
+{
+    if (value < -32768)
+        return kRegTypeInteger;
+    else if (value < -128)
+        return kRegTypeShort;
+    else if (value < 0)
+        return kRegTypeByte;
+    else if (value == 0)
+        return kRegTypeZero;
+    else if (value == 1)
+        return kRegTypeOne;
+    else if (value < 128)
+        return kRegTypePosByte;
+    else if (value < 32768)
+        return kRegTypePosShort;
+    else if (value < 65536)
+        return kRegTypeChar;
+    else
+        return kRegTypeInteger;
+}
+
+/*
  * Create a new uninitialized instance map.
  *
  * The map is allocated and populated with address entries.  The addresses
@@ -2337,9 +2363,10 @@ static RegType mergeTypes(RegType type1, RegType type2, bool* pChanged)
      * Use the table if we can, and reject any attempts to merge something
      * from the table with a reference type.
      *
-     * The uninitialized table entry at index zero *will* show up as a
-     * simple kRegTypeUninit value.  Since this cannot be merged with
-     * anything but itself, the rules do the right thing.
+     * Uninitialized references are composed of the enum ORed with an
+     * index value.  The uninitialized table entry at index zero *will*
+     * show up as a simple kRegTypeUninit value.  Since this cannot be
+     * merged with anything but itself, the rules do the right thing.
      */
     if (type1 < kRegTypeMAX) {
         if (type2 < kRegTypeMAX) {
@@ -2416,6 +2443,9 @@ static void updateRegisters(const Method* meth, InsnFlags* insnFlags,
         LOGVV("COPY into 0x%04x\n", nextInsn);
         copyRegisters(targetRegs, workRegs, insnRegCount + kExtraRegs);
         dvmInsnSetChanged(insnFlags, nextInsn, true);
+#ifdef VERIFIER_STATS
+        gDvm.verifierStats.copyRegCount++;
+#endif
     } else {
         if (gDebugVerbose) {
             LOGVV("MERGE into 0x%04x\n", nextInsn);
@@ -2426,6 +2456,8 @@ static void updateRegisters(const Method* meth, InsnFlags* insnFlags,
         bool changed = false;
         int i;
 
+        assert(dvmInsnIsBranchTarget(insnFlags, nextInsn));
+
         for (i = 0; i < insnRegCount + kExtraRegs; i++) {
             targetRegs[i] = mergeTypes(targetRegs[i], workRegs[i], &changed);
         }
@@ -2434,6 +2466,11 @@ static void updateRegisters(const Method* meth, InsnFlags* insnFlags,
             //LOGI(" RESULT (changed=%d)\n", changed);
             //dumpRegTypes(meth, insnFlags, targetRegs, 0, "rslt", NULL, 0);
         }
+#ifdef VERIFIER_STATS
+        gDvm.verifierStats.mergeRegCount++;
+        if (changed)
+            gDvm.verifierStats.mergeRegChanged++;
+#endif
 
         if (changed)
             dvmInsnSetChanged(insnFlags, nextInsn, true);
@@ -3043,7 +3080,7 @@ bail:
  */
 
 /*
- * Entry point for the detailed code-flow analysis.
+ * Entry point for the detailed code-flow analysis of a single method.
  */
 bool dvmVerifyCodeFlow(VerifierData* vdata)
 {
@@ -3055,6 +3092,10 @@ bool dvmVerifyCodeFlow(VerifierData* vdata)
 
     memset(&regTable, 0, sizeof(regTable));
 
+#ifdef VERIFIER_STATS
+    gDvm.verifierStats.methodsExamined++;
+#endif
+
 #ifndef NDEBUG
     checkMergeTab();     // only need to do this if table gets updated
 #endif
@@ -3432,6 +3473,14 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,
     const u2* insns = meth->insns + insnIdx;
     bool result = false;
 
+#ifdef VERIFIER_STATS
+    if (dvmInsnIsVisited(insnFlags, insnIdx)) {
+        gDvm.verifierStats.instrsReexamined++;
+    } else {
+        gDvm.verifierStats.instrsExamined++;
+    }
+#endif
+
     /*
      * Once we finish decoding the instruction, we need to figure out where
      * we can go from here.  There are three possible ways to transfer
@@ -3655,12 +3704,12 @@ static bool verifyInstruction(const Method* meth, InsnFlags* insnFlags,
     case OP_CONST:
         /* could be boolean, int, float, or a null reference */
         setRegisterType(workRegs, decInsn.vA,
-            dvmDetermineCat1Const((s4)decInsn.vB));
+            determineCat1Const((s4)decInsn.vB));
         break;
     case OP_CONST_HIGH16:
         /* could be boolean, int, float, or a null reference */
         setRegisterType(workRegs, decInsn.vA,
-            dvmDetermineCat1Const((s4) decInsn.vB << 16));
+            determineCat1Const((s4) decInsn.vB << 16));
         break;
     case OP_CONST_WIDE_16:
     case OP_CONST_WIDE_32:
index 51c4b9a..e7106ce 100644 (file)
@@ -913,6 +913,16 @@ static void verifyAndOptimizeClasses(DexFile* pDexFile, bool doVerify,
         dvmFreeInlineSubsTable(gDvm.inlineSubs);
         gDvm.inlineSubs = NULL;
     }
+
+#ifdef VERIFIER_STATS
+    LOGI("Verifier stats:\n");
+    LOGI(" methods examined        : %u\n", gDvm.verifierStats.methodsExamined);
+    LOGI(" instructions examined   : %u\n", gDvm.verifierStats.instrsExamined);
+    LOGI(" instructions re-examined: %u\n", gDvm.verifierStats.instrsReexamined);
+    LOGI(" copying of register sets: %u\n", gDvm.verifierStats.copyRegCount);
+    LOGI(" merging of register sets: %u\n", gDvm.verifierStats.mergeRegCount);
+    LOGI(" ...that caused changes  : %u\n", gDvm.verifierStats.mergeRegChanged);
+#endif
 }
 
 /*
index 123c195..213b674 100644 (file)
@@ -20,6 +20,7 @@
  */
 #include "Dalvik.h"
 #include "analysis/CodeVerify.h"
+#include "libdex/DexCatch.h"
 
 
 /* fwd */
@@ -92,6 +93,152 @@ bool dvmVerifyClass(ClassObject* clazz)
 
 
 /*
+ * Compute the width of the instruction at each address in the instruction
+ * stream.  Addresses that are in the middle of an instruction, or that
+ * are part of switch table data, are not set (so the caller should probably
+ * initialize "insnFlags" to zero).
+ *
+ * If "pNewInstanceCount" is not NULL, it will be set to the number of
+ * new-instance instructions in the method.
+ *
+ * Performs some static checks, notably:
+ * - opcode of first instruction begins at index 0
+ * - only documented instructions may appear
+ * - each instruction follows the last
+ * - last byte of last instruction is at (code_length-1)
+ *
+ * Logs an error and returns "false" on failure.
+ */
+static bool computeCodeWidths(const Method* meth, InsnFlags* insnFlags,
+    int* pNewInstanceCount)
+{
+    size_t insnCount = dvmGetMethodInsnsSize(meth);
+    const u2* insns = meth->insns;
+    bool result = false;
+    int newInstanceCount = 0;
+    int i;
+
+
+    for (i = 0; i < (int) insnCount; /**/) {
+        size_t width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
+        if (width == 0) {
+            LOG_VFY_METH(meth,
+                "VFY: invalid post-opt instruction (0x%04x)\n", *insns);
+            goto bail;
+        }
+
+        if ((*insns & 0xff) == OP_NEW_INSTANCE)
+            newInstanceCount++;
+
+        if (width > 65535) {
+            LOG_VFY_METH(meth, "VFY: insane width %d\n", width);
+            goto bail;
+        }
+
+        insnFlags[i] |= width;
+        i += width;
+        insns += width;
+    }
+    if (i != (int) dvmGetMethodInsnsSize(meth)) {
+        LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)\n",
+            i, dvmGetMethodInsnsSize(meth));
+        goto bail;
+    }
+
+    result = true;
+    if (pNewInstanceCount != NULL)
+        *pNewInstanceCount = newInstanceCount;
+
+bail:
+    return result;
+}
+
+/*
+ * Set the "in try" flags for all instructions protected by "try" statements.
+ * Also sets the "branch target" flags for exception handlers.
+ *
+ * Call this after widths have been set in "insnFlags".
+ *
+ * Returns "false" if something in the exception table looks fishy, but
+ * we're expecting the exception table to be somewhat sane.
+ */
+static bool scanTryCatchBlocks(const Method* meth, InsnFlags* insnFlags)
+{
+    u4 insnsSize = dvmGetMethodInsnsSize(meth);
+    const DexCode* pCode = dvmGetMethodCode(meth);
+    u4 triesSize = pCode->triesSize;
+    const DexTry* pTries;
+    u4 handlersSize;
+    u4 offset;
+    u4 i;
+
+    if (triesSize == 0) {
+        return true;
+    }
+
+    pTries = dexGetTries(pCode);
+    handlersSize = dexGetHandlersSize(pCode);
+
+    for (i = 0; i < triesSize; i++) {
+        const DexTry* pTry = &pTries[i];
+        u4 start = pTry->startAddr;
+        u4 end = start + pTry->insnCount;
+        u4 addr;
+
+        if ((start >= end) || (start >= insnsSize) || (end > insnsSize)) {
+            LOG_VFY_METH(meth,
+                "VFY: bad exception entry: startAddr=%d endAddr=%d (size=%d)\n",
+                start, end, insnsSize);
+            return false;
+        }
+
+        if (dvmInsnGetWidth(insnFlags, start) == 0) {
+            LOG_VFY_METH(meth,
+                "VFY: 'try' block starts inside an instruction (%d)\n",
+                start);
+            return false;
+        }
+
+        for (addr = start; addr < end;
+            addr += dvmInsnGetWidth(insnFlags, addr))
+        {
+            assert(dvmInsnGetWidth(insnFlags, addr) != 0);
+            dvmInsnSetInTry(insnFlags, addr, true);
+        }
+    }
+
+    /* Iterate over each of the handlers to verify target addresses. */
+    offset = dexGetFirstHandlerOffset(pCode);
+    for (i = 0; i < handlersSize; i++) {
+        DexCatchIterator iterator;
+        dexCatchIteratorInit(&iterator, pCode, offset);
+
+        for (;;) {
+            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+            u4 addr;
+
+            if (handler == NULL) {
+                break;
+            }
+
+            addr = handler->address;
+            if (dvmInsnGetWidth(insnFlags, addr) == 0) {
+                LOG_VFY_METH(meth,
+                    "VFY: exception handler starts at bad address (%d)\n",
+                    addr);
+                return false;
+            }
+
+            dvmInsnSetBranchTarget(insnFlags, addr, true);
+        }
+
+        offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+    }
+
+    return true;
+}
+
+/*
  * Perform verification on a single method.
  *
  * We do this in three passes:
@@ -109,7 +256,7 @@ bool dvmVerifyClass(ClassObject* clazz)
  * Confirmed here:
  * - code array must not be empty
  * - (N/A) code_length must be less than 65536
- * Confirmed by dvmComputeCodeWidths():
+ * Confirmed by computeCodeWidths():
  * - opcode of first instruction begins at index 0
  * - only documented instructions may appear
  * - each instruction follows the last
@@ -177,7 +324,7 @@ static bool verifyMethod(Method* meth)
      * Count up the #of occurrences of new-instance instructions while we're
      * at it.
      */
-    if (!dvmComputeCodeWidths(meth, vdata.insnFlags, &newInstanceCount))
+    if (!computeCodeWidths(meth, vdata.insnFlags, &newInstanceCount))
         goto bail;
 
     /*
@@ -191,7 +338,7 @@ static bool verifyMethod(Method* meth)
     /*
      * Set the "in try" flags for all instructions guarded by a "try" block.
      */
-    if (!dvmSetTryFlags(meth, vdata.insnFlags))
+    if (!scanTryCatchBlocks(meth, vdata.insnFlags))
         goto bail;
 
     /*
@@ -201,12 +348,10 @@ static bool verifyMethod(Method* meth)
         goto bail;
 
     /*
-     * Do code-flow analysis.  Do this after verifying the branch targets
-     * so we don't need to worry about it here.
+     * Do code-flow analysis.
      *
-     * If there are no registers, we don't need to do much in the way of
-     * analysis, but we still need to verify that nothing actually tries
-     * to use a register.
+     * We could probably skip this for a method with no registers, but
+     * that's so rare that there's little point in checking.
      */
     if (!dvmVerifyCodeFlow(&vdata)) {
         //LOGD("+++ %s failed code flow\n", meth->name);
index ab2af52..5df6970 100644 (file)
@@ -36,12 +36,6 @@ bool dvmVerificationStartup(void);
 void dvmVerificationShutdown(void);
 
 /*
- * Perform verification on all classes loaded from this DEX file.  If
- * enabled, it must happen before optimization.
- */
-bool dvmVerifyAllClasses(DexFile* pDexFile);
-
-/*
  * Verify a single class.
  */
 bool dvmVerifyClass(ClassObject* clazz);
@@ -51,4 +45,14 @@ bool dvmVerifyClass(ClassObject* clazz);
  */
 void dvmFreeRegisterMap(RegisterMap* pMap);
 
+/* some verifier counters, for debugging */
+typedef struct {
+    size_t  methodsExamined;    /* number of methods examined */
+    size_t  instrsExamined;     /* incr on first visit of instruction */
+    size_t  instrsReexamined;   /* incr on each repeat visit of instruction */
+    size_t  copyRegCount;       /* calls from updateRegisters->copyRegisters */
+    size_t  mergeRegCount;      /* calls from updateRegisters->merge */
+    size_t  mergeRegChanged;    /* calls from updateRegisters->merge, changed */
+} VerifierStats;
+
 #endif /*_DALVIK_DEXVERIFY*/
index f7d92cd..0da001f 100644 (file)
@@ -1835,1438 +1835,3 @@ bail:
     free(pNewMap);
     return NULL;
 }
-
-
-/*
- * ===========================================================================
- *      Just-in-time generation
- * ===========================================================================
- */
-
-#if 0   /* incomplete implementation; may be removed entirely in the future */
-
-/*
-Notes on just-in-time RegisterMap generation
-
-Generating RegisterMap tables as part of verification is convenient because
-we generate most of what we need to know as part of doing the verify.
-The negative aspect of doing it this way is that we must store the
-result in the DEX file (if we're verifying ahead of time) or in memory
-(if verifying during class load) for every concrete non-native method,
-even if we never actually need the map during a GC.
-
-A simple but compact encoding of register map data increases the size of
-optimized DEX files by about 25%, so size considerations are important.
-
-We can instead generate the RegisterMap at the point where it is needed.
-In a typical application we only need to convert about 2% of the loaded
-methods, and we can generate type-precise roots reasonably quickly because
-(a) we know the method has already been verified and hence can make a
-lot of assumptions, and (b) we don't care what type of object a register
-holds, just whether or not it holds a reference, and hence can skip a
-lot of class resolution gymnastics.
-
-There are a couple of problems with this approach however.  First, to
-get good performance we really want an implementation that is largely
-independent from the verifier, which means some duplication of effort.
-Second, we're dealing with post-dexopt code, which contains "quickened"
-instructions.  We can't process those without either tracking type
-information (which slows us down) or storing additional data in the DEX
-file that allows us to reconstruct the original instructions (adds ~5%
-to the size of the ODEX).
-
-
-Implementation notes...
-
-Both type-precise and live-precise information can be generated knowing
-only whether or not a register holds a reference.  We don't need to
-know what kind of reference or whether the object has been initialized.
-Not only can we skip many of the fancy steps in the verifier, we can
-initialize from simpler sources, e.g. the initial registers and return
-type are set from the "shorty" signature rather than the full signature.
-
-The short-term storage needs for just-in-time register map generation can
-be much lower because we can use a 1-byte SRegType instead of a 4-byte
-RegType.  On the other hand, if we're not doing type-precise analysis
-in the verifier we only need to store register contents at every branch
-target, rather than every GC point (which are much more frequent).
-
-Whether it happens in the verifier or independently, because this is done
-with native heap allocations that may be difficult to return to the system,
-an effort should be made to minimize memory use.
-*/
-
-/*
- * This is like RegType in the verifier, but simplified.  It holds a value
- * from the reg type enum, or kRegTypeReference.
- */
-typedef u1 SRegType;
-#define kRegTypeReference kRegTypeMAX
-
-/*
- * We need an extra "pseudo register" to hold the return type briefly.  It
- * can be category 1 or 2, so we need two slots.
- */
-#define kExtraRegs  2
-#define RESULT_REGISTER(_insnRegCountPlus)  (_insnRegCountPlus - kExtraRegs)
-
-/*
- * Working state.
- */
-typedef struct WorkState {
-    /*
-     * The method we're working on.
-     */
-    const Method* method;
-
-    /*
-     * Number of instructions in the method.
-     */
-    int         insnsSize;
-
-    /*
-     * Number of registers we track for each instruction.  This is equal
-     * to the method's declared "registersSize" plus kExtraRegs.
-     */
-    int         insnRegCountPlus;
-
-    /*
-     * Instruction widths and flags, one entry per code unit.
-     */
-    InsnFlags*  insnFlags;
-
-    /*
-     * Array of SRegType arrays, one entry per code unit.  We only need
-     * to create an entry when an instruction starts at this address.
-     * We can further reduce this to instructions that are GC points.
-     *
-     * We could just go ahead and allocate one per code unit, but for
-     * larger methods that can represent a significant bit of short-term
-     * storage.
-     */
-    SRegType**  addrRegs;
-
-    /*
-     * A single large alloc, with all of the storage needed for addrRegs.
-     */
-    SRegType*   regAlloc;
-} WorkState;
-
-// fwd
-static bool generateMap(WorkState* pState, RegisterMap* pMap);
-static bool analyzeMethod(WorkState* pState);
-static bool handleInstruction(WorkState* pState, SRegType* workRegs,\
-    int insnIdx, int* pStartGuess);
-static void updateRegisters(WorkState* pState, int nextInsn,\
-    const SRegType* workRegs);
-
-
-/*
- * Set instruction flags.
- */
-static bool setInsnFlags(WorkState* pState, int* pGcPointCount)
-{
-    const Method* meth = pState->method;
-    InsnFlags* insnFlags = pState->insnFlags;
-    int insnsSize = pState->insnsSize;
-    const u2* insns = meth->insns;
-    int gcPointCount = 0;
-    int offset;
-
-    /* set the widths */
-    if (!dvmComputeCodeWidths(meth, pState->insnFlags, NULL))
-        return false;
-
-    /* mark "try" regions and exception handler branch targets */
-    if (!dvmSetTryFlags(meth, pState->insnFlags))
-        return false;
-
-    /* the start of the method is a "branch target" */
-    dvmInsnSetBranchTarget(insnFlags, 0, true);
-
-    /*
-     * Run through the instructions, looking for switches and branches.
-     * Mark their targets.
-     *
-     * We don't really need to "check" these instructions -- the verifier
-     * already did that -- but the additional overhead isn't significant
-     * enough to warrant making a second copy of the "Check" function.
-     *
-     * Mark and count GC points while we're at it.
-     */
-    for (offset = 0; offset < insnsSize; offset++) {
-        static int gcMask = kInstrCanBranch | kInstrCanSwitch |
-            kInstrCanThrow | kInstrCanReturn;
-        u1 opcode = insns[offset] & 0xff;
-        InstructionFlags opFlags = dexGetInstrFlags(gDvm.instrFlags, opcode);
-
-        if (opFlags & kInstrCanBranch) {
-            if (!dvmCheckBranchTarget(meth, insnFlags, offset, true))
-                return false;
-        }
-        if (opFlags & kInstrCanSwitch) {
-            if (!dvmCheckSwitchTargets(meth, insnFlags, offset))
-                return false;
-        }
-
-        if ((opFlags & gcMask) != 0) {
-            dvmInsnSetGcPoint(pState->insnFlags, offset, true);
-            gcPointCount++;
-        }
-    }
-
-    *pGcPointCount = gcPointCount;
-    return true;
-}
-
-/*
- * Generate the register map for a method.
- *
- * Returns a pointer to newly-allocated storage.
- */
-RegisterMap* dvmGenerateRegisterMap(const Method* meth)
-{
-    WorkState* pState = NULL;
-    RegisterMap* pMap = NULL;
-    RegisterMap* result = NULL;
-    SRegType* regPtr;
-
-    pState = (WorkState*) calloc(1, sizeof(WorkState));
-    if (pState == NULL)
-        goto bail;
-
-    pMap = (RegisterMap*) calloc(1, sizeof(RegisterMap));
-    if (pMap == NULL)
-        goto bail;
-
-    pState->method = meth;
-    pState->insnsSize = dvmGetMethodInsnsSize(meth);
-    pState->insnRegCountPlus = meth->registersSize + kExtraRegs;
-
-    pState->insnFlags = calloc(sizeof(InsnFlags), pState->insnsSize);
-    pState->addrRegs = calloc(sizeof(SRegType*), pState->insnsSize);
-
-    /*
-     * Set flags on instructions, and calculate the number of code units
-     * that happen to be GC points.
-     */
-    int gcPointCount;
-    if (!setInsnFlags(pState, &gcPointCount))
-        goto bail;
-
-    if (gcPointCount == 0) {
-        /* the method doesn't allocate or call, and never returns? unlikely */
-        LOG_VFY_METH(meth, "Found do-nothing method\n");
-        goto bail;
-    }
-
-    pState->regAlloc = (SRegType*)
-        calloc(sizeof(SRegType), pState->insnsSize * gcPointCount);
-    regPtr = pState->regAlloc;
-
-    /*
-     * For each instruction that is a GC point, set a pointer into the
-     * regAlloc buffer.
-     */
-    int offset;
-    for (offset = 0; offset < pState->insnsSize; offset++) {
-        if (dvmInsnIsGcPoint(pState->insnFlags, offset)) {
-            pState->addrRegs[offset] = regPtr;
-            regPtr += pState->insnRegCountPlus;
-        }
-    }
-    assert(regPtr - pState->regAlloc == pState->insnsSize * gcPointCount);
-    assert(pState->addrRegs[0] != NULL);
-
-    /*
-     * Compute the register map.
-     */
-    if (!generateMap(pState, pMap))
-        goto bail;
-
-    /* success */
-    result = pMap;
-    pMap = NULL;
-
-bail:
-    if (pState != NULL) {
-        free(pState->insnFlags);
-        free(pState->addrRegs);
-        free(pState->regAlloc);
-        free(pState);
-    }
-    if (pMap != NULL)
-        dvmFreeRegisterMap(pMap);
-    return result;
-}
-
-/*
- * Release the storage associated with a RegisterMap.
- */
-void dvmFreeRegisterMap(RegisterMap* pMap)
-{
-    if (pMap == NULL)
-        return;
-}
-
-
-/*
- * Create the RegisterMap using the provided state.
- */
-static bool generateMap(WorkState* pState, RegisterMap* pMap)
-{
-    bool result = false;
-
-    /*
-     * Analyze the method and store the results in WorkState.
-     */
-    if (!analyzeMethod(pState))
-        goto bail;
-
-    /*
-     * Convert the analyzed data into a RegisterMap.
-     */
-    // TODO
-
-    result = true;
-
-bail:
-    return result;
-}
-
-/*
- * Set the register types for the method arguments.  We can pull the values
- * out of the "shorty" signature.
- */
-static bool setTypesFromSignature(WorkState* pState)
-{
-    const Method* meth = pState->method;
-    int argReg = meth->registersSize - meth->insSize;   /* first arg */
-    SRegType* pRegs = pState->addrRegs[0];
-    SRegType* pCurReg = &pRegs[argReg];
-    const char* ccp;
-
-    /*
-     * Include "this" pointer, if appropriate.
-     */
-    if (!dvmIsStaticMethod(meth)) {
-        *pCurReg++ = kRegTypeReference;
-    }
-
-    ccp = meth->shorty +1;      /* skip first byte, which holds return type */
-    while (*ccp != 0) {
-        switch (*ccp) {
-        case 'L':
-        //case '[':
-            *pCurReg++ = kRegTypeReference;
-            break;
-        case 'Z':
-            *pCurReg++ = kRegTypeBoolean;
-            break;
-        case 'C':
-            *pCurReg++ = kRegTypeChar;
-            break;
-        case 'B':
-            *pCurReg++ = kRegTypeByte;
-            break;
-        case 'I':
-            *pCurReg++ = kRegTypeInteger;
-            break;
-        case 'S':
-            *pCurReg++ = kRegTypeShort;
-            break;
-        case 'F':
-            *pCurReg++ = kRegTypeFloat;
-            break;
-        case 'D':
-            *pCurReg++ = kRegTypeDoubleLo;
-            *pCurReg++ = kRegTypeDoubleHi;
-            break;
-        case 'J':
-            *pCurReg++ = kRegTypeLongLo;
-            *pCurReg++ = kRegTypeLongHi;
-            break;
-        default:
-            assert(false);
-            return false;
-        }
-    }
-
-    assert(pCurReg - pRegs == meth->insSize);
-    return true;
-}
-
-/*
- * Find the start of the register set for the specified instruction in
- * the current method.
- */
-static inline SRegType* getRegisterLine(const WorkState* pState, int insnIdx)
-{
-    return pState->addrRegs[insnIdx];
-}
-
-/*
- * Copy a set of registers.
- */
-static inline void copyRegisters(SRegType* dst, const SRegType* src,
-    int numRegs)
-{
-    memcpy(dst, src, numRegs * sizeof(SRegType));
-}
-
-/*
- * Compare a set of registers.  Returns 0 if they match.
- */
-static inline int compareRegisters(const SRegType* src1, const SRegType* src2,
-    int numRegs)
-{
-    return memcmp(src1, src2, numRegs * sizeof(SRegType));
-}
-
-/*
- * Run through the instructions repeatedly until we have exercised all
- * possible paths.
- */
-static bool analyzeMethod(WorkState* pState)
-{
-    const Method* meth = pState->method;
-    SRegType workRegs[pState->insnRegCountPlus];
-    InsnFlags* insnFlags = pState->insnFlags;
-    int insnsSize = pState->insnsSize;
-    int insnIdx, startGuess;
-    bool result = false;
-
-    /*
-     * Initialize the types of the registers that correspond to method
-     * arguments.
-     */
-    if (!setTypesFromSignature(pState))
-        goto bail;
-
-    /*
-     * Mark the first instruction as "changed".
-     */
-    dvmInsnSetChanged(insnFlags, 0, true);
-    startGuess = 0;
-
-    if (true) {
-        IF_LOGI() {
-            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
-            LOGI("Now mapping: %s.%s %s (ins=%d regs=%d)\n",
-                meth->clazz->descriptor, meth->name, desc,
-                meth->insSize, meth->registersSize);
-            LOGI(" ------ [0    4    8    12   16   20   24   28   32   36\n");
-            free(desc);
-        }
-    }
-
-    /*
-     * Continue until no instructions are marked "changed".
-     */
-    while (true) {
-        /*
-         * Find the first marked one.  Use "startGuess" as a way to find
-         * one quickly.
-         */
-        for (insnIdx = startGuess; insnIdx < insnsSize; insnIdx++) {
-            if (dvmInsnIsChanged(insnFlags, insnIdx))
-                break;
-        }
-
-        if (insnIdx == insnsSize) {
-            if (startGuess != 0) {
-                /* try again, starting from the top */
-                startGuess = 0;
-                continue;
-            } else {
-                /* all flags are clear */
-                break;
-            }
-        }
-
-        /*
-         * We carry the working set of registers from instruction to
-         * instruction.  If this address can be the target of a branch
-         * (or throw) instruction, or if we're skipping around chasing
-         * "changed" flags, we need to load the set of registers from
-         * the table.
-         *
-         * Because we always prefer to continue on to the next instruction,
-         * we should never have a situation where we have a stray
-         * "changed" flag set on an instruction that isn't a branch target.
-         */
-        if (dvmInsnIsBranchTarget(insnFlags, insnIdx)) {
-            SRegType* insnRegs = getRegisterLine(pState, insnIdx);
-            assert(insnRegs != NULL);
-            copyRegisters(workRegs, insnRegs, pState->insnRegCountPlus);
-
-        } else {
-#ifndef NDEBUG
-            /*
-             * Sanity check: retrieve the stored register line (assuming
-             * a full table) and make sure it actually matches.
-             */
-            SRegType* insnRegs = getRegisterLine(pState, insnIdx);
-            if (insnRegs != NULL &&
-                compareRegisters(workRegs, insnRegs,
-                                 pState->insnRegCountPlus) != 0)
-            {
-                char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
-                LOG_VFY("HUH? workRegs diverged in %s.%s %s\n",
-                        meth->clazz->descriptor, meth->name, desc);
-                free(desc);
-            }
-#endif
-        }
-
-        /*
-         * Update the register sets altered by this instruction.
-         */
-        if (!handleInstruction(pState, workRegs, insnIdx, &startGuess)) {
-            goto bail;
-        }
-
-        dvmInsnSetVisited(insnFlags, insnIdx, true);
-        dvmInsnSetChanged(insnFlags, insnIdx, false);
-    }
-
-    // TODO - add dead code scan to help validate this code?
-
-    result = true;
-
-bail:
-    return result;
-}
-
-/*
- * Get a pointer to the method being invoked.
- *
- * Returns NULL on failure.
- */
-static Method* getInvokedMethod(const Method* meth,
-    const DecodedInstruction* pDecInsn, MethodType methodType)
-{
-    Method* resMethod;
-    char* sigOriginal = NULL;
-
-    /*
-     * Resolve the method.  This could be an abstract or concrete method
-     * depending on what sort of call we're making.
-     */
-    if (methodType == METHOD_INTERFACE) {
-        resMethod = dvmOptResolveInterfaceMethod(meth->clazz, pDecInsn->vB);
-    } else {
-        resMethod = dvmOptResolveMethod(meth->clazz, pDecInsn->vB, methodType);
-    }
-    if (resMethod == NULL) {
-        /* failed; print a meaningful failure message */
-        DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
-        const DexMethodId* pMethodId;
-        const char* methodName;
-        char* methodDesc;
-        const char* classDescriptor;
-
-        pMethodId = dexGetMethodId(pDexFile, pDecInsn->vB);
-        methodName = dexStringById(pDexFile, pMethodId->nameIdx);
-        methodDesc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
-        classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
-
-        LOG_VFY("VFY: unable to resolve %s method %u: %s.%s %s\n",
-            dvmMethodTypeStr(methodType), pDecInsn->vB,
-            classDescriptor, methodName, methodDesc);
-        free(methodDesc);
-        return NULL;
-    }
-
-    return resMethod;
-}
-
-/*
- * Return the register type for the method.  Since we don't care about
- * the actual type, we can just look at the "shorty" signature.
- *
- * Returns kRegTypeUnknown for "void".
- */
-static SRegType getMethodReturnType(const Method* meth)
-{
-    SRegType type;
-
-    switch (meth->shorty[0]) {
-    case 'I':
-        type = kRegTypeInteger;
-        break;
-    case 'C':
-        type = kRegTypeChar;
-        break;
-    case 'S':
-        type = kRegTypeShort;
-        break;
-    case 'B':
-        type = kRegTypeByte;
-        break;
-    case 'Z':
-        type = kRegTypeBoolean;
-        break;
-    case 'V':
-        type = kRegTypeUnknown;
-        break;
-    case 'F':
-        type = kRegTypeFloat;
-        break;
-    case 'D':
-        type = kRegTypeDoubleLo;
-        break;
-    case 'J':
-        type = kRegTypeLongLo;
-        break;
-    case 'L':
-    //case '[':
-        type = kRegTypeReference;
-        break;
-    default:
-        /* we verified signature return type earlier, so this is impossible */
-        assert(false);
-        type = kRegTypeConflict;
-        break;
-    }
-
-    return type;
-}
-
-/*
- * Copy a category 1 register.
- */
-static inline void copyRegister1(SRegType* insnRegs, u4 vdst, u4 vsrc)
-{
-    insnRegs[vdst] = insnRegs[vsrc];
-}
-
-/*
- * Copy a category 2 register.  Note the source and destination may overlap.
- */
-static inline void copyRegister2(SRegType* insnRegs, u4 vdst, u4 vsrc)
-{
-    //memmove(&insnRegs[vdst], &insnRegs[vsrc], sizeof(SRegType) * 2);
-    SRegType r1 = insnRegs[vsrc];
-    SRegType r2 = insnRegs[vsrc+1];
-    insnRegs[vdst] = r1;
-    insnRegs[vdst+1] = r2;
-}
-
-/*
- * Set the type of a category 1 register.
- */
-static inline void setRegisterType(SRegType* insnRegs, u4 vdst, SRegType type)
-{
-    insnRegs[vdst] = type;
-}
-
-/*
- * Decode the specified instruction and update the register info.
- */
-static bool handleInstruction(WorkState* pState, SRegType* workRegs,
-    int insnIdx, int* pStartGuess)
-{
-    const Method* meth = pState->method;
-    const u2* insns = meth->insns + insnIdx;
-    InsnFlags* insnFlags = pState->insnFlags;
-    bool result = false;
-
-    /*
-     * Once we finish decoding the instruction, we need to figure out where
-     * we can go from here.  There are three possible ways to transfer
-     * control to another statement:
-     *
-     * (1) Continue to the next instruction.  Applies to all but
-     *     unconditional branches, method returns, and exception throws.
-     * (2) Branch to one or more possible locations.  Applies to branches
-     *     and switch statements.
-     * (3) Exception handlers.  Applies to any instruction that can
-     *     throw an exception that is handled by an encompassing "try"
-     *     block.  (We simplify this to be any instruction that can
-     *     throw any exception.)
-     *
-     * We can also return, in which case there is no successor instruction
-     * from this point.
-     *
-     * The behavior can be determined from the InstrFlags.
-     */
-    DecodedInstruction decInsn;
-    SRegType entryRegs[pState->insnRegCountPlus];
-    const int insnRegCountPlus = pState->insnRegCountPlus;
-    bool justSetResult = false;
-    int branchTarget = 0;
-    SRegType tmpType;
-
-    dexDecodeInstruction(gDvm.instrFormat, insns, &decInsn);
-    const int nextFlags = dexGetInstrFlags(gDvm.instrFlags, decInsn.opCode);
-
-    /*
-     * Make a copy of the previous register state.  If the instruction
-     * throws an exception, we merge *this* into the destination rather
-     * than workRegs, because we don't want the result from the "successful"
-     * code path (e.g. a check-cast that "improves" a type) to be visible
-     * to the exception handler.
-     */
-    if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
-    {
-        copyRegisters(entryRegs, workRegs, insnRegCountPlus);
-    }
-
-    switch (decInsn.opCode) {
-    case OP_NOP:
-        break;
-
-    case OP_MOVE:
-    case OP_MOVE_FROM16:
-    case OP_MOVE_16:
-    case OP_MOVE_OBJECT:
-    case OP_MOVE_OBJECT_FROM16:
-    case OP_MOVE_OBJECT_16:
-        copyRegister1(workRegs, decInsn.vA, decInsn.vB);
-        break;
-    case OP_MOVE_WIDE:
-    case OP_MOVE_WIDE_FROM16:
-    case OP_MOVE_WIDE_16:
-        copyRegister2(workRegs, decInsn.vA, decInsn.vB);
-        break;
-
-    /*
-     * The move-result instructions copy data out of a "pseudo-register"
-     * with the results from the last method invocation.  In practice we
-     * might want to hold the result in an actual CPU register, so the
-     * Dalvik spec requires that these only appear immediately after an
-     * invoke or filled-new-array.
-     *
-     * These calls invalidate the "result" register.  (This is now
-     * redundant with the reset done below, but it can make the debug info
-     * easier to read in some cases.)
-     */
-    case OP_MOVE_RESULT:
-    case OP_MOVE_RESULT_OBJECT:
-        copyRegister1(workRegs, decInsn.vA, RESULT_REGISTER(insnRegCountPlus));
-        break;
-    case OP_MOVE_RESULT_WIDE:
-        copyRegister2(workRegs, decInsn.vA, RESULT_REGISTER(insnRegCountPlus));
-        break;
-
-    case OP_MOVE_EXCEPTION:
-        /*
-         * This statement can only appear as the first instruction in an
-         * exception handler (though not all exception handlers need to
-         * have one of these).  We verify that as part of extracting the
-         * exception type from the catch block list.
-         */
-        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
-        break;
-
-    case OP_RETURN_VOID:
-    case OP_RETURN:
-    case OP_RETURN_WIDE:
-    case OP_RETURN_OBJECT:
-        break;
-
-    case OP_CONST_4:
-    case OP_CONST_16:
-    case OP_CONST:
-        /* could be boolean, int, float, or a null reference */
-        setRegisterType(workRegs, decInsn.vA,
-            dvmDetermineCat1Const((s4)decInsn.vB));
-        break;
-    case OP_CONST_HIGH16:
-        /* could be boolean, int, float, or a null reference */
-        setRegisterType(workRegs, decInsn.vA,
-            dvmDetermineCat1Const((s4) decInsn.vB << 16));
-        break;
-    case OP_CONST_WIDE_16:
-    case OP_CONST_WIDE_32:
-    case OP_CONST_WIDE:
-    case OP_CONST_WIDE_HIGH16:
-        /* could be long or double; default to long and allow conversion */
-        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
-        break;
-    case OP_CONST_STRING:
-    case OP_CONST_STRING_JUMBO:
-    case OP_CONST_CLASS:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
-        break;
-
-    case OP_MONITOR_ENTER:
-    case OP_MONITOR_EXIT:
-        break;
-
-    case OP_CHECK_CAST:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
-        break;
-    case OP_INSTANCE_OF:
-        /* result is boolean */
-        setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
-        break;
-
-    case OP_ARRAY_LENGTH:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
-        break;
-
-    case OP_NEW_INSTANCE:
-    case OP_NEW_ARRAY:
-        /* add the new uninitialized reference to the register ste */
-        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
-        break;
-    case OP_FILLED_NEW_ARRAY:
-    case OP_FILLED_NEW_ARRAY_RANGE:
-        setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
-            kRegTypeReference);
-        justSetResult = true;
-        break;
-
-    case OP_CMPL_FLOAT:
-    case OP_CMPG_FLOAT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
-        break;
-    case OP_CMPL_DOUBLE:
-    case OP_CMPG_DOUBLE:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
-        break;
-    case OP_CMP_LONG:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeBoolean);
-        break;
-
-    case OP_THROW:
-    case OP_GOTO:
-    case OP_GOTO_16:
-    case OP_GOTO_32:
-    case OP_PACKED_SWITCH:
-    case OP_SPARSE_SWITCH:
-        break;
-
-    case OP_FILL_ARRAY_DATA:
-        break;
-
-    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:
-        break;
-
-    case OP_AGET:
-        tmpType = kRegTypeInteger;
-        goto aget_1nr_common;
-    case OP_AGET_BOOLEAN:
-        tmpType = kRegTypeBoolean;
-        goto aget_1nr_common;
-    case OP_AGET_BYTE:
-        tmpType = kRegTypeByte;
-        goto aget_1nr_common;
-    case OP_AGET_CHAR:
-        tmpType = kRegTypeChar;
-        goto aget_1nr_common;
-    case OP_AGET_SHORT:
-        tmpType = kRegTypeShort;
-        goto aget_1nr_common;
-aget_1nr_common:
-        setRegisterType(workRegs, decInsn.vA, tmpType);
-        break;
-
-    case OP_AGET_WIDE:
-        /*
-         * We know this is either long or double, and we don't really
-         * discriminate between those during verification, so we
-         * call it a long.
-         */
-        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
-        break;
-
-    case OP_AGET_OBJECT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
-        break;
-
-    case OP_APUT:
-    case OP_APUT_BOOLEAN:
-    case OP_APUT_BYTE:
-    case OP_APUT_CHAR:
-    case OP_APUT_SHORT:
-    case OP_APUT_WIDE:
-    case OP_APUT_OBJECT:
-        break;
-
-    case OP_IGET:
-        tmpType = kRegTypeInteger;
-        goto iget_1nr_common;
-    case OP_IGET_BOOLEAN:
-        tmpType = kRegTypeBoolean;
-        goto iget_1nr_common;
-    case OP_IGET_BYTE:
-        tmpType = kRegTypeByte;
-        goto iget_1nr_common;
-    case OP_IGET_CHAR:
-        tmpType = kRegTypeChar;
-        goto iget_1nr_common;
-    case OP_IGET_SHORT:
-        tmpType = kRegTypeShort;
-        goto iget_1nr_common;
-iget_1nr_common:
-        setRegisterType(workRegs, decInsn.vA, tmpType);
-        break;
-
-    case OP_IGET_WIDE:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
-        break;
-
-    case OP_IGET_OBJECT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
-        break;
-
-    case OP_IPUT:
-    case OP_IPUT_BOOLEAN:
-    case OP_IPUT_BYTE:
-    case OP_IPUT_CHAR:
-    case OP_IPUT_SHORT:
-    case OP_IPUT_WIDE:
-    case OP_IPUT_OBJECT:
-        break;
-
-    case OP_SGET:
-        tmpType = kRegTypeInteger;
-        goto sget_1nr_common;
-    case OP_SGET_BOOLEAN:
-        tmpType = kRegTypeBoolean;
-        goto sget_1nr_common;
-    case OP_SGET_BYTE:
-        tmpType = kRegTypeByte;
-        goto sget_1nr_common;
-    case OP_SGET_CHAR:
-        tmpType = kRegTypeChar;
-        goto sget_1nr_common;
-    case OP_SGET_SHORT:
-        tmpType = kRegTypeShort;
-        goto sget_1nr_common;
-sget_1nr_common:
-        setRegisterType(workRegs, decInsn.vA, tmpType);
-        break;
-
-    case OP_SGET_WIDE:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
-        break;
-
-    case OP_SGET_OBJECT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeReference);
-        break;
-
-    case OP_SPUT:
-    case OP_SPUT_BOOLEAN:
-    case OP_SPUT_BYTE:
-    case OP_SPUT_CHAR:
-    case OP_SPUT_SHORT:
-    case OP_SPUT_WIDE:
-    case OP_SPUT_OBJECT:
-        break;
-
-    case OP_INVOKE_VIRTUAL:
-    case OP_INVOKE_VIRTUAL_RANGE:
-    case OP_INVOKE_SUPER:
-    case OP_INVOKE_SUPER_RANGE:
-        {
-            Method* calledMethod;
-
-            calledMethod = getInvokedMethod(meth, &decInsn, METHOD_VIRTUAL);
-            if (calledMethod == NULL)
-                goto bail;
-            setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
-                getMethodReturnType(calledMethod));
-            justSetResult = true;
-        }
-        break;
-    case OP_INVOKE_DIRECT:
-    case OP_INVOKE_DIRECT_RANGE:
-        {
-            Method* calledMethod;
-
-            calledMethod = getInvokedMethod(meth, &decInsn, METHOD_DIRECT);
-            if (calledMethod == NULL)
-                goto bail;
-            setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
-                getMethodReturnType(calledMethod));
-            justSetResult = true;
-        }
-        break;
-    case OP_INVOKE_STATIC:
-    case OP_INVOKE_STATIC_RANGE:
-        {
-            Method* calledMethod;
-
-            calledMethod = getInvokedMethod(meth, &decInsn, METHOD_STATIC);
-            if (calledMethod == NULL)
-                goto bail;
-            setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
-                getMethodReturnType(calledMethod));
-            justSetResult = true;
-        }
-        break;
-    case OP_INVOKE_INTERFACE:
-    case OP_INVOKE_INTERFACE_RANGE:
-        {
-            Method* absMethod;
-
-            absMethod = getInvokedMethod(meth, &decInsn, METHOD_INTERFACE);
-            if (absMethod == NULL)
-                goto bail;
-            setRegisterType(workRegs, RESULT_REGISTER(insnRegCountPlus),
-                getMethodReturnType(absMethod));
-            justSetResult = true;
-        }
-        break;
-
-    case OP_NEG_INT:
-    case OP_NOT_INT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
-        break;
-    case OP_NEG_LONG:
-    case OP_NOT_LONG:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
-        break;
-    case OP_NEG_FLOAT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
-        break;
-    case OP_NEG_DOUBLE:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
-        break;
-    case OP_INT_TO_LONG:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
-        break;
-    case OP_INT_TO_FLOAT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
-        break;
-    case OP_INT_TO_DOUBLE:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
-        break;
-    case OP_LONG_TO_INT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
-        break;
-    case OP_LONG_TO_FLOAT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
-        break;
-    case OP_LONG_TO_DOUBLE:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
-        break;
-    case OP_FLOAT_TO_INT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
-        break;
-    case OP_FLOAT_TO_LONG:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
-        break;
-    case OP_FLOAT_TO_DOUBLE:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
-        break;
-    case OP_DOUBLE_TO_INT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
-        break;
-    case OP_DOUBLE_TO_LONG:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
-        break;
-    case OP_DOUBLE_TO_FLOAT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
-        break;
-    case OP_INT_TO_BYTE:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeByte);
-        break;
-    case OP_INT_TO_CHAR:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeChar);
-        break;
-    case OP_INT_TO_SHORT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeShort);
-        break;
-
-    case OP_ADD_INT:
-    case OP_SUB_INT:
-    case OP_MUL_INT:
-    case OP_REM_INT:
-    case OP_DIV_INT:
-    case OP_SHL_INT:
-    case OP_SHR_INT:
-    case OP_USHR_INT:
-    case OP_AND_INT:
-    case OP_OR_INT:
-    case OP_XOR_INT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
-        break;
-    case OP_ADD_LONG:
-    case OP_SUB_LONG:
-    case OP_MUL_LONG:
-    case OP_DIV_LONG:
-    case OP_REM_LONG:
-    case OP_AND_LONG:
-    case OP_OR_LONG:
-    case OP_XOR_LONG:
-    case OP_SHL_LONG:
-    case OP_SHR_LONG:
-    case OP_USHR_LONG:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
-        break;
-    case OP_ADD_FLOAT:
-    case OP_SUB_FLOAT:
-    case OP_MUL_FLOAT:
-    case OP_DIV_FLOAT:
-    case OP_REM_FLOAT:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
-        break;
-    case OP_ADD_DOUBLE:
-    case OP_SUB_DOUBLE:
-    case OP_MUL_DOUBLE:
-    case OP_DIV_DOUBLE:
-    case OP_REM_DOUBLE:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
-        break;
-    case OP_ADD_INT_2ADDR:
-    case OP_SUB_INT_2ADDR:
-    case OP_MUL_INT_2ADDR:
-    case OP_REM_INT_2ADDR:
-    case OP_SHL_INT_2ADDR:
-    case OP_SHR_INT_2ADDR:
-    case OP_USHR_INT_2ADDR:
-    case OP_AND_INT_2ADDR:
-    case OP_OR_INT_2ADDR:
-    case OP_XOR_INT_2ADDR:
-    case OP_DIV_INT_2ADDR:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
-        break;
-    case OP_ADD_LONG_2ADDR:
-    case OP_SUB_LONG_2ADDR:
-    case OP_MUL_LONG_2ADDR:
-    case OP_DIV_LONG_2ADDR:
-    case OP_REM_LONG_2ADDR:
-    case OP_AND_LONG_2ADDR:
-    case OP_OR_LONG_2ADDR:
-    case OP_XOR_LONG_2ADDR:
-    case OP_SHL_LONG_2ADDR:
-    case OP_SHR_LONG_2ADDR:
-    case OP_USHR_LONG_2ADDR:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeLongLo);
-        break;
-    case OP_ADD_FLOAT_2ADDR:
-    case OP_SUB_FLOAT_2ADDR:
-    case OP_MUL_FLOAT_2ADDR:
-    case OP_DIV_FLOAT_2ADDR:
-    case OP_REM_FLOAT_2ADDR:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeFloat);
-        break;
-    case OP_ADD_DOUBLE_2ADDR:
-    case OP_SUB_DOUBLE_2ADDR:
-    case OP_MUL_DOUBLE_2ADDR:
-    case OP_DIV_DOUBLE_2ADDR:
-    case OP_REM_DOUBLE_2ADDR:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeDoubleLo);
-        break;
-    case OP_ADD_INT_LIT16:
-    case OP_RSUB_INT:
-    case OP_MUL_INT_LIT16:
-    case OP_DIV_INT_LIT16:
-    case OP_REM_INT_LIT16:
-    case OP_AND_INT_LIT16:
-    case OP_OR_INT_LIT16:
-    case OP_XOR_INT_LIT16:
-    case OP_ADD_INT_LIT8:
-    case OP_RSUB_INT_LIT8:
-    case OP_MUL_INT_LIT8:
-    case OP_DIV_INT_LIT8:
-    case OP_REM_INT_LIT8:
-    case OP_SHL_INT_LIT8:
-    case OP_SHR_INT_LIT8:
-    case OP_USHR_INT_LIT8:
-    case OP_AND_INT_LIT8:
-    case OP_OR_INT_LIT8:
-    case OP_XOR_INT_LIT8:
-        setRegisterType(workRegs, decInsn.vA, kRegTypeInteger);
-        break;
-
-
-    /*
-     * See comments in analysis/CodeVerify.c re: why some of these are
-     * annoying to deal with.  It's worse in this implementation, because
-     * we're not keeping any information about the classes held in each
-     * reference register.
-     *
-     * Handling most of these would require retaining the field/method
-     * reference info that we discarded when the instructions were
-     * quickened.  This is feasible but not currently supported.
-     */
-    case OP_EXECUTE_INLINE:
-    case OP_EXECUTE_INLINE_RANGE:
-    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_IGET_WIDE_VOLATILE:
-    case OP_IPUT_WIDE_VOLATILE:
-    case OP_SGET_WIDE_VOLATILE:
-    case OP_SPUT_WIDE_VOLATILE:
-    case OP_INVOKE_VIRTUAL_QUICK:
-    case OP_INVOKE_VIRTUAL_QUICK_RANGE:
-    case OP_INVOKE_SUPER_QUICK:
-    case OP_INVOKE_SUPER_QUICK_RANGE:
-        dvmAbort();     // not implemented, shouldn't be here
-        break;
-
-
-    /* these should never appear */
-    case OP_UNUSED_3E:
-    case OP_UNUSED_3F:
-    case OP_UNUSED_40:
-    case OP_UNUSED_41:
-    case OP_UNUSED_42:
-    case OP_UNUSED_43:
-    case OP_UNUSED_73:
-    case OP_UNUSED_79:
-    case OP_UNUSED_7A:
-    case OP_UNUSED_E3:
-    case OP_UNUSED_E4:
-    case OP_UNUSED_E5:
-    case OP_UNUSED_E6:
-    case OP_UNUSED_E7:
-    case OP_BREAKPOINT:
-    case OP_UNUSED_ED:
-    case OP_UNUSED_F1:
-    case OP_UNUSED_FC:
-    case OP_UNUSED_FD:
-    case OP_UNUSED_FE:
-    case OP_UNUSED_FF:
-        dvmAbort();
-        break;
-
-    /*
-     * DO NOT add a "default" clause here.  Without it the compiler will
-     * complain if an instruction is missing (which is desirable).
-     */
-    }
-
-
-    /*
-     * If we didn't just set the result register, clear it out.  This
-     * isn't so important here, but does help ensure that our output matches
-     * the verifier.
-     */
-    if (!justSetResult) {
-        int reg = RESULT_REGISTER(pState->insnRegCountPlus);
-        workRegs[reg] = workRegs[reg+1] = kRegTypeUnknown;
-    }
-
-    /*
-     * Handle "continue".  Tag the next consecutive instruction.
-     */
-    if ((nextFlags & kInstrCanContinue) != 0) {
-        int insnWidth = dvmInsnGetWidth(insnFlags, insnIdx);
-
-        /*
-         * We want to update the registers and set the "changed" flag on the
-         * next instruction (if necessary).  We aren't storing register
-         * changes for all addresses, so for non-GC-point targets we just
-         * compare "entry" vs. "work" to see if we've changed anything.
-         */
-        if (getRegisterLine(pState, insnIdx+insnWidth) != NULL) {
-            updateRegisters(pState, insnIdx+insnWidth, workRegs);
-        } else {
-            /* if not yet visited, or regs were updated, set "changed" */
-            if (!dvmInsnIsVisited(insnFlags, insnIdx+insnWidth) ||
-                compareRegisters(workRegs, entryRegs,
-                    pState->insnRegCountPlus) != 0)
-            {
-                dvmInsnSetChanged(insnFlags, insnIdx+insnWidth, true);
-            }
-        }
-    }
-
-    /*
-     * Handle "branch".  Tag the branch target.
-     */
-    if ((nextFlags & kInstrCanBranch) != 0) {
-        bool isConditional;
-
-        dvmGetBranchTarget(meth, insnFlags, insnIdx, &branchTarget,
-                &isConditional);
-        assert(isConditional || (nextFlags & kInstrCanContinue) == 0);
-        assert(!isConditional || (nextFlags & kInstrCanContinue) != 0);
-
-        updateRegisters(pState, insnIdx+branchTarget, workRegs);
-    }
-
-    /*
-     * Handle "switch".  Tag all possible branch targets.
-     */
-    if ((nextFlags & kInstrCanSwitch) != 0) {
-        int offsetToSwitch = insns[1] | (((s4)insns[2]) << 16);
-        const u2* switchInsns = insns + offsetToSwitch;
-        int switchCount = switchInsns[1];
-        int offsetToTargets, targ;
-
-        if ((*insns & 0xff) == OP_PACKED_SWITCH) {
-            /* 0=sig, 1=count, 2/3=firstKey */
-            offsetToTargets = 4;
-        } else {
-            /* 0=sig, 1=count, 2..count*2 = keys */
-            assert((*insns & 0xff) == OP_SPARSE_SWITCH);
-            offsetToTargets = 2 + 2*switchCount;
-        }
-
-        /* verify each switch target */
-        for (targ = 0; targ < switchCount; targ++) {
-            int offset, absOffset;
-
-            /* offsets are 32-bit, and only partly endian-swapped */
-            offset = switchInsns[offsetToTargets + targ*2] |
-                     (((s4) switchInsns[offsetToTargets + targ*2 +1]) << 16);
-            absOffset = insnIdx + offset;
-            assert(absOffset >= 0 && absOffset < pState->insnsSize);
-
-            updateRegisters(pState, absOffset, workRegs);
-        }
-    }
-
-    /*
-     * Handle instructions that can throw and that are sitting in a
-     * "try" block.  (If they're not in a "try" block when they throw,
-     * control transfers out of the method.)
-     */
-    if ((nextFlags & kInstrCanThrow) != 0 && dvmInsnIsInTry(insnFlags, insnIdx))
-    {
-        DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
-        const DexCode* pCode = dvmGetMethodCode(meth);
-        DexCatchIterator iterator;
-
-        if (dexFindCatchHandler(&iterator, pCode, insnIdx)) {
-            while (true) {
-                DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
-                if (handler == NULL)
-                    break;
-
-                /* note we use entryRegs, not workRegs */
-                updateRegisters(pState, handler->address, entryRegs);
-            }
-        }
-    }
-
-    /*
-     * Update startGuess.  Advance to the next instruction of that's
-     * possible, otherwise use the branch target if one was found.  If
-     * neither of those exists we're in a return or throw; leave startGuess
-     * alone and let the caller sort it out.
-     */
-    if ((nextFlags & kInstrCanContinue) != 0) {
-        *pStartGuess = insnIdx + dvmInsnGetWidth(insnFlags, insnIdx);
-    } else if ((nextFlags & kInstrCanBranch) != 0) {
-        /* we're still okay if branchTarget is zero */
-        *pStartGuess = insnIdx + branchTarget;
-    }
-
-    assert(*pStartGuess >= 0 && *pStartGuess < pState->insnsSize &&
-        dvmInsnGetWidth(insnFlags, *pStartGuess) != 0);
-
-    result = true;
-
-bail:
-    return result;
-}
-
-
-/*
- * Merge two SRegType values.
- *
- * Sets "*pChanged" to "true" if the result doesn't match "type1".
- */
-static SRegType mergeTypes(SRegType type1, SRegType type2, bool* pChanged)
-{
-    SRegType result;
-
-    /*
-     * Check for trivial case so we don't have to hit memory.
-     */
-    if (type1 == type2)
-        return type1;
-
-    /*
-     * Use the table if we can, and reject any attempts to merge something
-     * from the table with a reference type.
-     *
-     * The uninitialized table entry at index zero *will* show up as a
-     * simple kRegTypeUninit value.  Since this cannot be merged with
-     * anything but itself, the rules do the right thing.
-     */
-    if (type1 < kRegTypeMAX) {
-        if (type2 < kRegTypeMAX) {
-            result = gDvmMergeTab[type1][type2];
-        } else {
-            /* simple + reference == conflict, usually */
-            if (type1 == kRegTypeZero)
-                result = type2;
-            else
-                result = kRegTypeConflict;
-        }
-    } else {
-        if (type2 < kRegTypeMAX) {
-            /* reference + simple == conflict, usually */
-            if (type2 == kRegTypeZero)
-                result = type1;
-            else
-                result = kRegTypeConflict;
-        } else {
-            /* merging two references */
-            assert(type1 == type2);
-            result = type1;
-        }
-    }
-
-    if (result != type1)
-        *pChanged = true;
-    return result;
-}
-
-/*
- * Control can transfer to "nextInsn".
- *
- * Merge the registers from "workRegs" into "addrRegs" at "nextInsn", and
- * set the "changed" flag on the target address if the registers have changed.
- */
-static void updateRegisters(WorkState* pState, int nextInsn,
-    const SRegType* workRegs)
-{
-    const Method* meth = pState->method;
-    InsnFlags* insnFlags = pState->insnFlags;
-    const int insnRegCountPlus = pState->insnRegCountPlus;
-    SRegType* targetRegs = getRegisterLine(pState, nextInsn);
-
-    if (!dvmInsnIsVisitedOrChanged(insnFlags, nextInsn)) {
-        /*
-         * We haven't processed this instruction before, and we haven't
-         * touched the registers here, so there's nothing to "merge".  Copy
-         * the registers over and mark it as changed.  (This is the only
-         * way a register can transition out of "unknown", so this is not
-         * just an optimization.)
-         */
-        LOGVV("COPY into 0x%04x\n", nextInsn);
-        copyRegisters(targetRegs, workRegs, insnRegCountPlus);
-        dvmInsnSetChanged(insnFlags, nextInsn, true);
-    } else {
-        /* merge registers, set Changed only if different */
-        LOGVV("MERGE into 0x%04x\n", nextInsn);
-        bool changed = false;
-        int i;
-
-        for (i = 0; i < insnRegCountPlus; i++) {
-            targetRegs[i] = mergeTypes(targetRegs[i], workRegs[i], &changed);
-        }
-
-        if (changed)
-            dvmInsnSetChanged(insnFlags, nextInsn, true);
-    }
-}
-
-#endif /*#if 0*/
index e9c4715..02dd9ba 100644 (file)
  */
 #include "Dalvik.h"
 #include "analysis/CodeVerify.h"
-#include "libdex/DexCatch.h"
 #include "libdex/InstrUtils.h"
 
 
 /*
- * Compute the width of the instruction at each address in the instruction
- * stream.  Addresses that are in the middle of an instruction, or that
- * are part of switch table data, are not set (so the caller should probably
- * initialize "insnFlags" to zero).
- *
- * If "pNewInstanceCount" is not NULL, it will be set to the number of
- * new-instance instructions in the method.
- *
- * Performs some static checks, notably:
- * - opcode of first instruction begins at index 0
- * - only documented instructions may appear
- * - each instruction follows the last
- * - last byte of last instruction is at (code_length-1)
- *
- * Logs an error and returns "false" on failure.
- */
-bool dvmComputeCodeWidths(const Method* meth, InsnFlags* insnFlags,
-    int* pNewInstanceCount)
-{
-    size_t insnCount = dvmGetMethodInsnsSize(meth);
-    const u2* insns = meth->insns;
-    bool result = false;
-    int newInstanceCount = 0;
-    int i;
-
-
-    for (i = 0; i < (int) insnCount; /**/) {
-        size_t width = dexGetInstrOrTableWidthAbs(gDvm.instrWidth, insns);
-        if (width == 0) {
-            LOG_VFY_METH(meth,
-                "VFY: invalid post-opt instruction (0x%04x)\n", *insns);
-            goto bail;
-        }
-
-        if ((*insns & 0xff) == OP_NEW_INSTANCE)
-            newInstanceCount++;
-
-        if (width > 65535) {
-            LOG_VFY_METH(meth, "VFY: insane width %d\n", width);
-            goto bail;
-        }
-
-        insnFlags[i] |= width;
-        i += width;
-        insns += width;
-    }
-    if (i != (int) dvmGetMethodInsnsSize(meth)) {
-        LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)\n",
-            i, dvmGetMethodInsnsSize(meth));
-        goto bail;
-    }
-
-    result = true;
-    if (pNewInstanceCount != NULL)
-        *pNewInstanceCount = newInstanceCount;
-
-bail:
-    return result;
-}
-
-/*
- * Set the "in try" flags for all instructions protected by "try" statements.
- * Also sets the "branch target" flags for exception handlers.
- *
- * Call this after widths have been set in "insnFlags".
- *
- * Returns "false" if something in the exception table looks fishy, but
- * we're expecting the exception table to be somewhat sane.
- */
-bool dvmSetTryFlags(const Method* meth, InsnFlags* insnFlags)
-{
-    u4 insnsSize = dvmGetMethodInsnsSize(meth);
-    const DexCode* pCode = dvmGetMethodCode(meth);
-    u4 triesSize = pCode->triesSize;
-    const DexTry* pTries;
-    u4 handlersSize;
-    u4 offset;
-    u4 i;
-
-    if (triesSize == 0) {
-        return true;
-    }
-
-    pTries = dexGetTries(pCode);
-    handlersSize = dexGetHandlersSize(pCode);
-
-    for (i = 0; i < triesSize; i++) {
-        const DexTry* pTry = &pTries[i];
-        u4 start = pTry->startAddr;
-        u4 end = start + pTry->insnCount;
-        u4 addr;
-
-        if ((start >= end) || (start >= insnsSize) || (end > insnsSize)) {
-            LOG_VFY_METH(meth,
-                "VFY: bad exception entry: startAddr=%d endAddr=%d (size=%d)\n",
-                start, end, insnsSize);
-            return false;
-        }
-
-        if (dvmInsnGetWidth(insnFlags, start) == 0) {
-            LOG_VFY_METH(meth,
-                "VFY: 'try' block starts inside an instruction (%d)\n",
-                start);
-            return false;
-        }
-
-        for (addr = start; addr < end;
-            addr += dvmInsnGetWidth(insnFlags, addr))
-        {
-            assert(dvmInsnGetWidth(insnFlags, addr) != 0);
-            dvmInsnSetInTry(insnFlags, addr, true);
-        }
-    }
-
-    /* Iterate over each of the handlers to verify target addresses. */
-    offset = dexGetFirstHandlerOffset(pCode);
-    for (i = 0; i < handlersSize; i++) {
-        DexCatchIterator iterator;
-        dexCatchIteratorInit(&iterator, pCode, offset);
-
-        for (;;) {
-            DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
-            u4 addr;
-
-            if (handler == NULL) {
-                break;
-            }
-
-            addr = handler->address;
-            if (dvmInsnGetWidth(insnFlags, addr) == 0) {
-                LOG_VFY_METH(meth,
-                    "VFY: exception handler starts at bad address (%d)\n",
-                    addr);
-                return false;
-            }
-
-            dvmInsnSetBranchTarget(insnFlags, addr, true);
-        }
-
-        offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
-    }
-
-    return true;
-}
-
-/*
  * Output a code verifier warning message.  For the pre-verifier it's not
  * a big deal if something fails (and it may even be expected), but if
  * we're doing just-in-time verification it's significant.
@@ -266,29 +119,3 @@ bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
 
     return true;
 }
-
-/*
- * Given a 32-bit constant, return the most-restricted RegType enum entry
- * that can hold the value.
- */
-char dvmDetermineCat1Const(s4 value)
-{
-    if (value < -32768)
-        return kRegTypeInteger;
-    else if (value < -128)
-        return kRegTypeShort;
-    else if (value < 0)
-        return kRegTypeByte;
-    else if (value == 0)
-        return kRegTypeZero;
-    else if (value == 1)
-        return kRegTypeOne;
-    else if (value < 128)
-        return kRegTypePosByte;
-    else if (value < 32768)
-        return kRegTypePosShort;
-    else if (value < 65536)
-        return kRegTypeChar;
-    else
-        return kRegTypeInteger;
-}