Progress on liveness analysis.

Compute basic blocks and their predecessors, necessary for backward flow
analysis.  This is a work in progress and isn't yet enabled.

(When enabled, verification takes 20% longer, so there's some room for
improvement on performance.)

Also, this changes the "generate register maps" setting to be enabled
by default, and allows a "no" prefix on -Xgenregmap to disable it.

Bug 2534655

Change-Id: Id2e8512f53fc454ce2184879ab663ed7121274b6
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
index 140372d..43cdc41 100644
--- a/vm/Dvm.mk
+++ b/vm/Dvm.mk
@@ -141,6 +141,7 @@
 	analysis/Optimize.c \
 	analysis/RegisterMap.c \
 	analysis/VerifySubs.c \
+	analysis/VfyBasicBlock.c \
 	hprof/Hprof.c \
 	hprof/HprofClass.c \
 	hprof/HprofHeap.c \
diff --git a/vm/Globals.h b/vm/Globals.h
index c2639cb..bea7c2c 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -62,6 +62,22 @@
 } ExecutionMode;
 
 /*
+ * Register map generation mode.  Only applicable when generateRegisterMaps
+ * is enabled.  (The "disabled" state is not folded into this because
+ * there are callers like dexopt that want to enable/disable without
+ * specifying the configuration details.)
+ *
+ * "TypePrecise" is slower and requires additional storage for the register
+ * maps, but allows type-precise GC.  "LivePrecise" is even slower and
+ * requires additional heap during processing, but allows live-precise GC.
+ */
+typedef enum {
+    kRegisterMapModeUnknown = 0,
+    kRegisterMapModeTypePrecise,
+    kRegisterMapModeLivePrecise
+} RegisterMapMode;
+
+/*
  * All fields are initialized to zero.
  *
  * Storage allocated here must be freed by a subsystem shutdown function or
@@ -118,6 +134,9 @@
     DexOptimizerMode    dexOptMode;
     DexClassVerifyMode  classVerifyMode;
 
+    bool        generateRegisterMaps;
+    RegisterMapMode     registerMapMode;
+
     bool        monitorVerification;
 
     bool        dexOptForSmp;
@@ -128,7 +147,6 @@
     bool        preciseGc;
     bool        preVerify;
     bool        postVerify;
-    bool        generateRegisterMaps;
     bool        concurrentMarkSweep;
     bool        verifyCardTable;
 
diff --git a/vm/Init.c b/vm/Init.c
index e1e7711..741799d 100644
--- a/vm/Init.c
+++ b/vm/Init.c
@@ -119,7 +119,7 @@
     dvmFprintf(stderr, "  -Xgc:[no]postverify\n");
     dvmFprintf(stderr, "  -Xgc:[no]concurrent\n");
     dvmFprintf(stderr, "  -Xgc:[no]verifycardtable\n");
-    dvmFprintf(stderr, "  -Xgenregmap\n");
+    dvmFprintf(stderr, "  -X[no]genregmap\n");
     dvmFprintf(stderr, "  -Xverifyopt:[no]checkmon\n");
     dvmFprintf(stderr, "  -Xcheckdexsum\n");
 #if defined(WITH_JIT)
@@ -983,7 +983,8 @@
 
         } else if (strcmp(argv[i], "-Xgenregmap") == 0) {
             gDvm.generateRegisterMaps = true;
-            LOGV("Register maps will be generated during verification\n");
+        } else if (strcmp(argv[i], "-Xnogenregmap") == 0) {
+            gDvm.generateRegisterMaps = false;
 
         } else if (strcmp(argv[i], "Xverifyopt:checkmon") == 0) {
             gDvm.monitorVerification = true;
@@ -1083,6 +1084,8 @@
     gDvm.classVerifyMode = VERIFY_MODE_ALL;
     gDvm.dexOptMode = OPTIMIZE_MODE_VERIFIED;
     gDvm.monitorVerification = false;
+    gDvm.generateRegisterMaps = true;
+    gDvm.registerMapMode = kRegisterMapModeTypePrecise;
 
     /*
      * Default execution mode.
diff --git a/vm/PointerSet.c b/vm/PointerSet.c
index 862f733..ca7b537 100644
--- a/vm/PointerSet.c
+++ b/vm/PointerSet.c
@@ -267,7 +267,8 @@
  */
 void dvmPointerSetDump(const PointerSet* pSet)
 {
+    LOGI("PointerSet %p\n", pSet);
     int i;
     for (i = 0; i < pSet->count; i++)
-        printf(" %p", pSet->list[i]);
+        LOGI(" %2d: %p", i, pSet->list[i]);
 }
diff --git a/vm/analysis/CodeVerify.c b/vm/analysis/CodeVerify.c
index 3d9d270..aa41d82 100644
--- a/vm/analysis/CodeVerify.c
+++ b/vm/analysis/CodeVerify.c
@@ -3445,6 +3445,14 @@
     vdata->registerLines = NULL;     /* don't set this until we need it */
 
     /*
+     * Compute basic blocks if we want to do liveness analysis.
+     */
+    if (gDvm.registerMapMode == kRegisterMapModeLivePrecise) {
+        if (!dvmComputeVfyBasicBlocks(vdata))
+            goto bail;
+    }
+
+    /*
      * Initialize the types of the registers that correspond to the
      * method arguments.  We can determine this from the method signature.
      */
@@ -3763,7 +3771,7 @@
     RegisterLine* workLine = &regTable->workLine;
     const DexFile* pDexFile = meth->clazz->pDvmDex->pDexFile;
     ClassObject* resClass;
-    int branchTarget = 0;
+    s4 branchTarget = 0;
     const int insnRegCount = meth->registersSize;
     RegType tmpType;
     DecodedInstruction decInsn;
@@ -5754,7 +5762,7 @@
     if ((nextFlags & kInstrCanBranch) != 0) {
         bool isConditional;
 
-        if (!dvmGetBranchTarget(meth, insnFlags, insnIdx, &branchTarget,
+        if (!dvmGetBranchOffset(meth, insnFlags, insnIdx, &branchTarget,
                 &isConditional))
         {
             /* should never happen after static verification */
diff --git a/vm/analysis/CodeVerify.h b/vm/analysis/CodeVerify.h
index 85b2e5e..b9147ef 100644
--- a/vm/analysis/CodeVerify.h
+++ b/vm/analysis/CodeVerify.h
@@ -21,6 +21,7 @@
 #define _DALVIK_CODEVERIFY
 
 #include "analysis/VerifySubs.h"
+#include "analysis/VfyBasicBlock.h"
 
 
 /*
@@ -187,6 +188,12 @@
      */
     size_t          newInstanceCount;
     size_t          monitorEnterCount;
+
+    /*
+     * Array of pointers to basic blocks, one entry per code unit.  Used
+     * for liveness analysis.
+     */
+    VfyBasicBlock** basicBlocks;
 } VerifierData;
 
 
diff --git a/vm/analysis/DexVerify.c b/vm/analysis/DexVerify.c
index 2e2d267..1a931ad 100644
--- a/vm/analysis/DexVerify.c
+++ b/vm/analysis/DexVerify.c
@@ -111,7 +111,7 @@
         i += width;
         insns += width;
     }
-    if (i != (int) dvmGetMethodInsnsSize(meth)) {
+    if (i != (int) vdata->insnsSize) {
         LOG_VFY_METH(meth, "VFY: code did not end where expected (%d vs. %d)\n",
             i, dvmGetMethodInsnsSize(meth));
         goto bail;
@@ -140,19 +140,16 @@
     const DexCode* pCode = dvmGetMethodCode(meth);
     u4 triesSize = pCode->triesSize;
     const DexTry* pTries;
-    u4 handlersSize;
-    u4 offset;
-    u4 i;
+    u4 idx;
 
     if (triesSize == 0) {
         return true;
     }
 
     pTries = dexGetTries(pCode);
-    handlersSize = dexGetHandlersSize(pCode);
 
-    for (i = 0; i < triesSize; i++) {
-        const DexTry* pTry = &pTries[i];
+    for (idx = 0; idx < triesSize; idx++) {
+        const DexTry* pTry = &pTries[idx];
         u4 start = pTry->startAddr;
         u4 end = start + pTry->insnCount;
         u4 addr;
@@ -180,8 +177,9 @@
     }
 
     /* Iterate over each of the handlers to verify target addresses. */
-    offset = dexGetFirstHandlerOffset(pCode);
-    for (i = 0; i < handlersSize; i++) {
+    u4 handlersSize = dexGetHandlersSize(pCode);
+    u4 offset = dexGetFirstHandlerOffset(pCode);
+    for (idx = 0; idx < handlersSize; idx++) {
         DexCatchIterator iterator;
         dexCatchIteratorInit(&iterator, pCode, offset);
 
@@ -252,6 +250,7 @@
     vdata.insnRegCount = meth->registersSize;
     vdata.insnFlags = NULL;
     vdata.uninitMap = NULL;
+    vdata.basicBlocks = NULL;
 
     /*
      * If there aren't any instructions, make sure that's expected, then
@@ -285,8 +284,7 @@
      * TODO: Consider keeping a reusable pre-allocated array sitting
      * around for smaller methods.
      */
-    vdata.insnFlags = (InsnFlags*)
-        calloc(dvmGetMethodInsnsSize(meth), sizeof(InsnFlags));
+    vdata.insnFlags = (InsnFlags*) calloc(vdata.insnsSize, sizeof(InsnFlags));
     if (vdata.insnFlags == NULL)
         goto bail;
 
@@ -307,12 +305,14 @@
 
     /*
      * Set the "in try" flags for all instructions guarded by a "try" block.
+     * Also sets the "branch target" flag on exception handlers.
      */
     if (!scanTryCatchBlocks(meth, vdata.insnFlags))
         goto bail;
 
     /*
-     * Perform static instruction verification.
+     * Perform static instruction verification.  Also sets the "branch
+     * target" flags.
      */
     if (!verifyInstructions(&vdata))
         goto bail;
@@ -332,6 +332,7 @@
     result = true;
 
 bail:
+    dvmFreeVfyBasicBlocks(&vdata);
     dvmFreeUninitInstanceMap(vdata.uninitMap);
     free(vdata.insnFlags);
     return result;
@@ -717,10 +718,10 @@
     int curOffset, bool selfOkay)
 {
     const int insnCount = dvmGetMethodInsnsSize(meth);
-    int offset, absOffset;
+    s4 offset, absOffset;
     bool isConditional;
 
-    if (!dvmGetBranchTarget(meth, insnFlags, curOffset, &offset,
+    if (!dvmGetBranchOffset(meth, insnFlags, curOffset, &offset,
             &isConditional))
         return false;
 
@@ -1208,8 +1209,12 @@
              * This instruction is probably a GC point.  Branch instructions
              * only qualify if they go backward, so for those we need to
              * check the offset.
+             *
+             * TODO: we could also scan the targets of a "switch" statement,
+             * and if none of them branch backward we could ignore that
+             * instruction as well.
              */
-            int offset;
+            s4 offset;
             bool unused;
             if ((opFlags & kInstrCanBranch) != 0) {
                 /*
@@ -1217,7 +1222,7 @@
                  * component was tagged with kVfyBranch, but it's easier
                  * to just grab it again than cart the state around.
                  */
-                if (!dvmGetBranchTarget(meth, insnFlags, codeOffset, &offset,
+                if (!dvmGetBranchOffset(meth, insnFlags, codeOffset, &offset,
                         &unused))
                 {
                     /* should never happen */
diff --git a/vm/analysis/VerifySubs.c b/vm/analysis/VerifySubs.c
index df1dcaf..cbe8af7 100644
--- a/vm/analysis/VerifySubs.c
+++ b/vm/analysis/VerifySubs.c
@@ -79,8 +79,8 @@
  *
  * Returns "false" on failure (e.g. this isn't a branch instruction).
  */
-bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
-    int curOffset, int* pOffset, bool* pConditional)
+bool dvmGetBranchOffset(const Method* meth, const InsnFlags* insnFlags,
+    int curOffset, s4* pOffset, bool* pConditional)
 {
     const u2* insns = meth->insns + curOffset;
 
diff --git a/vm/analysis/VerifySubs.h b/vm/analysis/VerifySubs.h
index f145fff..9449e64 100644
--- a/vm/analysis/VerifySubs.h
+++ b/vm/analysis/VerifySubs.h
@@ -61,9 +61,9 @@
 void dvmLogUnableToResolveClass(const char* missingClassDescr,
     const Method* meth);
 
-/* extract the relative branch target from a branch instruction */
-bool dvmGetBranchTarget(const Method* meth, InsnFlags* insnFlags,
-    int curOffset, int* pOffset, bool* pConditional);
+/* extract the relative branch offset from a branch instruction */
+bool dvmGetBranchOffset(const Method* meth, const InsnFlags* insnFlags,
+    int curOffset, s4* pOffset, bool* pConditional);
 
 /* return a RegType enumeration value that "value" just fits into */
 char dvmDetermineCat1Const(s4 value);
diff --git a/vm/analysis/VfyBasicBlock.c b/vm/analysis/VfyBasicBlock.c
new file mode 100644
index 0000000..59d395e
--- /dev/null
+++ b/vm/analysis/VfyBasicBlock.c
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Verifier basic block functions.
+ */
+#include "Dalvik.h"
+#include "analysis/VfyBasicBlock.h"
+#include "analysis/CodeVerify.h"
+#include "analysis/VerifySubs.h"
+#include "libdex/DexCatch.h"
+#include "libdex/InstrUtils.h"
+
+
+/*
+ * Extract the list of catch handlers from "pTry" into "addrBuf".
+ *
+ * Returns the size of the catch handler list.  If the return value
+ * exceeds "addrBufSize", the items at the end of the list will not be
+ * represented in the output array, and this function should be called
+ * again with a larger buffer.
+ */
+static u4 extractCatchHandlers(const DexCode* pCode, const DexTry* pTry,
+    u4* addrBuf, size_t addrBufSize)
+{
+    DexCatchIterator iterator;
+    unsigned int idx = 0;
+
+    dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
+    while (true) {
+        DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+        if (handler == NULL)
+            break;
+
+        if (idx < addrBufSize) {
+            addrBuf[idx] = handler->address;
+        }
+        idx++;
+    }
+
+    return idx;
+}
+
+/*
+ * Returns "true" if the instruction represents a data chunk, such as a
+ * switch statement block.
+ */
+static bool isDataChunk(u2 insn)
+{
+    return (insn == kPackedSwitchSignature ||
+            insn == kSparseSwitchSignature ||
+            insn == kArrayDataSignature);
+}
+
+/*
+ * Alloc a basic block in the specified slot.  The storage will be
+ * initialized.
+ */
+static VfyBasicBlock* allocVfyBasicBlock(VerifierData* vdata, u4 idx)
+{
+    VfyBasicBlock* newBlock = (VfyBasicBlock*) calloc(1, sizeof(VfyBasicBlock));
+    if (newBlock == NULL)
+        return NULL;
+
+    newBlock->predecessors = dvmPointerSetAlloc(8);
+    if (newBlock->predecessors == NULL) {
+        free(newBlock);
+        return NULL;
+    }
+
+    newBlock->firstAddr = (u4) -1;      // DEBUG
+
+    return newBlock;
+}
+
+/*
+ * Add "curBlock" to the predecessor list in "targetIdx".
+ */
+static bool addToPredecessor(VerifierData* vdata, VfyBasicBlock* curBlock,
+    u4 targetIdx)
+{
+    assert(targetIdx < vdata->insnsSize);
+
+    /*
+     * Allocate the target basic block if necessary.  This will happen
+     * on e.g. forward branches.
+     *
+     * We can't fill in all the fields, but that will happen automatically
+     * when we get to that part of the code.
+     */
+    VfyBasicBlock* targetBlock = vdata->basicBlocks[targetIdx];
+    if (targetBlock == NULL) {
+        targetBlock = allocVfyBasicBlock(vdata, targetIdx);
+        if (targetBlock == NULL)
+            return false;
+        vdata->basicBlocks[targetIdx] = targetBlock;
+    }
+
+    PointerSet* preds = targetBlock->predecessors;
+    bool added = dvmPointerSetAddEntry(preds, curBlock);
+    if (!added) {
+        /*
+         * This happens sometimes for packed-switch instructions, where
+         * the same target address appears more than once.  Also, a
+         * (pointless) conditional branch to the next instruction will
+         * trip over this.
+         */
+        LOGV("ODD: point set for targ=0x%04x (%p) already had block "
+             "fir=0x%04x (%p)\n",
+            targetIdx, targetBlock, curBlock->firstAddr, curBlock);
+    }
+
+    return true;
+}
+
+/*
+ * Add ourselves to the predecessor list in all blocks we might transfer
+ * control to.
+ *
+ * There are four ways to proceed to a new instruction:
+ *  (1) continue to the following instruction
+ *  (2) [un]conditionally branch to a specific location
+ *  (3) conditionally branch through a "switch" statement
+ *  (4) throw an exception
+ *
+ * Returning from the method (via a return statement or an uncaught
+ * exception) are not interesting for liveness analysis.
+ */
+static bool setPredecessors(VerifierData* vdata, VfyBasicBlock* curBlock,
+    u4 curIdx, OpcodeFlags opFlags, u4 nextIdx, u4* handlerList,
+    size_t numHandlers)
+{
+    const InsnFlags* insnFlags = vdata->insnFlags;
+    const Method* meth = vdata->method;
+
+    unsigned int handlerIdx;
+    for (handlerIdx = 0; handlerIdx < numHandlers; handlerIdx++) {
+        if (!addToPredecessor(vdata, curBlock, handlerList[handlerIdx]))
+            return false;
+    }
+
+    if ((opFlags & kInstrCanContinue) != 0) {
+        if (!addToPredecessor(vdata, curBlock, nextIdx))
+            return false;
+    }
+    if ((opFlags & kInstrCanBranch) != 0) {
+        bool unused, gotBranch;
+        s4 branchOffset, absOffset;
+
+        gotBranch = dvmGetBranchOffset(meth, insnFlags, curIdx,
+                &branchOffset, &unused);
+        assert(gotBranch);
+        absOffset = curIdx + branchOffset;
+        assert(absOffset >= 0 && (u4) absOffset < vdata->insnsSize);
+
+        if (!addToPredecessor(vdata, curBlock, absOffset))
+            return false;
+    }
+
+    if ((opFlags & kInstrCanSwitch) != 0) {
+        const u2* curInsn = &meth->insns[curIdx];
+        const u2* dataPtr;
+
+        /* these values have already been verified, so we can trust them */
+        s4 offsetToData = curInsn[1] | ((s4) curInsn[2]) << 16;
+        dataPtr = curInsn + offsetToData;
+
+        /*
+         * dataPtr points to the start of the switch data.  The first
+         * item is the NOP+magic, the second is the number of entries in
+         * the switch table.
+         */
+        u2 switchCount = dataPtr[1];
+
+        /*
+         * Skip past the ident field, size field, and the first_key field
+         * (for packed) or the key list (for sparse).
+         */
+        if (dexOpcodeFromCodeUnit(meth->insns[curIdx]) == OP_PACKED_SWITCH) {
+            dataPtr += 4;
+        } else {
+            assert(dexOpcodeFromCodeUnit(meth->insns[curIdx]) ==
+                    OP_SPARSE_SWITCH);
+            dataPtr += 2 + 2 * switchCount;
+        }
+
+        u4 switchIdx;
+        for (switchIdx = 0; switchIdx < switchCount; switchIdx++) {
+            s4 offset, absOffset;
+
+            offset = (s4) dataPtr[switchIdx*2] |
+                     (s4) (dataPtr[switchIdx*2 +1] << 16);
+            absOffset = curIdx + offset;
+            assert(absOffset >= 0 && (u4) absOffset < vdata->insnsSize);
+
+            if (!addToPredecessor(vdata, curBlock, absOffset))
+                return false;
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Dump the contents of the basic blocks.
+ */
+static void dumpBasicBlocks(const VerifierData* vdata)
+{
+    char printBuf[256];
+    unsigned int idx;
+    int count;
+
+    LOGI("Basic blocks for %s.%s:%s\n", vdata->method->clazz->descriptor,
+        vdata->method->name, vdata->method->shorty);
+    for (idx = 0; idx < vdata->insnsSize; idx++) {
+        VfyBasicBlock* block = vdata->basicBlocks[idx];
+        if (block == NULL)
+            continue;
+
+        assert(block->firstAddr == idx);
+        count = snprintf(printBuf, sizeof(printBuf), " %04x-%04x ",
+            block->firstAddr, block->lastAddr);
+
+        PointerSet* preds = block->predecessors;
+        size_t numDeps = dvmPointerSetGetCount(preds);
+
+        if (numDeps > 0) {
+            count += snprintf(printBuf + count, sizeof(printBuf) - count,
+                    "preds:");
+
+            unsigned int predIdx;
+            for (predIdx = 0; predIdx < numDeps; predIdx++) {
+                if (count >= (int) sizeof(printBuf))
+                    break;
+                const VfyBasicBlock* pred =
+                    (const VfyBasicBlock*) dvmPointerSetGetEntry(preds, predIdx);
+                count += snprintf(printBuf + count, sizeof(printBuf) - count,
+                        "%04x(%p),", pred->firstAddr, pred);
+            }
+        } else {
+            count += snprintf(printBuf + count, sizeof(printBuf) - count,
+                    "(no preds)");
+        }
+
+        LOGI("%s", printBuf);
+    }
+}
+
+
+/*
+ * Generate a list of basic blocks and related information.
+ *
+ * On success, returns "true" with vdata->basicBlocks initialized.
+ */
+bool dvmComputeVfyBasicBlocks(VerifierData* vdata)
+{
+    const InsnFlags* insnFlags = vdata->insnFlags;
+    const Method* meth = vdata->method;
+    const u4 insnsSize = vdata->insnsSize;
+    const DexCode* pCode = dvmGetMethodCode(meth);
+    const DexTry* pTries = NULL;
+    const size_t kHandlerStackAllocSize = 16;   /* max seen so far is 7 */
+    u4 handlerAddrs[kHandlerStackAllocSize];
+    u4* handlerListAlloc = NULL;
+    u4* handlerList = NULL;
+    size_t numHandlers = 0;
+    u4 idx, blockStartAddr;
+    bool result = false;
+
+    bool verbose = false;
+    if (false) {
+        if (strcmp(meth->clazz->descriptor, "Ljava/security/Provider;") == 0 &&
+            strcmp(meth->name, "entrySet") == 0)
+        {
+            verbose = true;
+        }
+    }
+
+    if (verbose) {
+        LOGI("Basic blocks for %s.%s:%s\n",
+            meth->clazz->descriptor, meth->name, meth->shorty);
+    }
+
+    /*
+     * Allocate a data structure that allows us to map from an address to
+     * the corresponding basic block.  Initially all pointers are NULL.
+     * They are populated on demand as we proceed (either when we reach a
+     * new BB, or when we need to add an item to the predecessor list in
+     * a not-yet-reached BB).
+     *
+     * Only the first instruction in the block points to the BB structure;
+     * the rest remain NULL.
+     */
+    vdata->basicBlocks =
+        (VfyBasicBlock**) calloc(insnsSize, sizeof(VfyBasicBlock*));
+    if (vdata->basicBlocks == NULL)
+        goto bail;
+
+    /*
+     * The "tries" list is a series of non-overlapping regions with a list
+     * of "catch" handlers.  Rather than do the "find a matching try block"
+     * computation at each step, we just walk the "try" list in parallel.
+     *
+     * Not all methods have "try" blocks.  If this one does, we init tryEnd
+     * to zero, so that the (exclusive bound) range check trips immediately.
+     */
+    u4 tryIndex = 0, tryStart = 0, tryEnd = 0;
+    if (pCode->triesSize != 0) {
+        pTries = dexGetTries(pCode);
+    }
+
+    u4 debugBBIndex = 0;
+
+    /*
+     * The address associated with a basic block is the start address.
+     */
+    blockStartAddr = 0;
+
+    for (idx = 0; idx < insnsSize; ) {
+        /*
+         * Make sure we're pointing at the right "try" block.  It should
+         * not be possible to "jump over" a block, so if we're no longer
+         * in the correct one we can just advance to the next.
+         */
+        if (pTries != NULL && idx >= tryEnd) {
+            if (tryIndex == pCode->triesSize) {
+                /* no more try blocks in this method */
+                pTries = NULL;
+                numHandlers = 0;
+            } else {
+                /*
+                 * Extract the set of handlers.  We want to avoid doing
+                 * this for each block, so we copy them to local storage.
+                 * If it doesn't fit in the small stack area, we'll use
+                 * the heap instead.
+                 *
+                 * It's rare to encounter a method with more than half a
+                 * dozen possible handlers.
+                 */
+                tryStart = pTries[tryIndex].startAddr;
+                tryEnd = tryStart + pTries[tryIndex].insnCount;
+
+                if (handlerListAlloc != NULL) {
+                    free(handlerListAlloc);
+                    handlerListAlloc = NULL;
+                }
+                numHandlers = extractCatchHandlers(pCode, &pTries[tryIndex],
+                    handlerAddrs, kHandlerStackAllocSize);
+                assert(numHandlers > 0);    // TODO make sure this is verified
+                if (numHandlers <= kHandlerStackAllocSize) {
+                    handlerList = handlerAddrs;
+                } else {
+                    LOGD("overflow, numHandlers=%d\n", numHandlers);
+                    handlerListAlloc = (u4*) malloc(sizeof(u4) * numHandlers);
+                    if (handlerListAlloc == NULL)
+                        return false;
+                    extractCatchHandlers(pCode, &pTries[tryIndex],
+                        handlerListAlloc, numHandlers);
+                    handlerList = handlerListAlloc;
+                }
+
+                LOGV("+++ start=%x end=%x numHan=%d\n",
+                    tryStart, tryEnd, numHandlers);
+
+                tryIndex++;
+            }
+        }
+
+        /*
+         * Check the current instruction, and possibly aspects of the
+         * next instruction, to see if this instruction ends the current
+         * basic block.
+         *
+         * Instructions that can throw only end the block if there is the
+         * possibility of a local handler catching the exception.
+         */
+        Opcode opcode = dexOpcodeFromCodeUnit(meth->insns[idx]);
+        OpcodeFlags opFlags = dexGetFlagsFromOpcode(opcode);
+        size_t nextIdx = idx + dexGetWidthFromInstruction(&meth->insns[idx]);
+        bool endBB = false;
+        bool ignoreInstr = false;
+
+        if ((opFlags & kInstrCanContinue) == 0) {
+            /* does not continue */
+            endBB = true;
+        } else if ((opFlags & (kInstrCanBranch | kInstrCanSwitch)) != 0) {
+            /* conditionally branches elsewhere */
+            endBB = true;
+        } else if ((opFlags & kInstrCanThrow) != 0 &&
+                dvmInsnIsInTry(insnFlags, idx))
+        {
+            /* throws an exception that might be caught locally */
+            endBB = true;
+        } else if (isDataChunk(meth->insns[idx])) {
+            /*
+             * If this is a data chunk (e.g. switch data) we want to skip
+             * over it entirely.  Set endBB so we don't carry this along as
+             * the start of a block, and ignoreInstr so we don't try to
+             * open a basic block for this instruction.
+             */
+            endBB = ignoreInstr = true;
+        } else if (dvmInsnIsBranchTarget(insnFlags, nextIdx)) {
+            /*
+             * We also need to end it if the next instruction is a branch
+             * target.  Note we've tagged exception catch blocks as such.
+             *
+             * If we're this far along in the "else" chain, we know that
+             * this isn't a data-chunk NOP, and control can continue to
+             * the next instruction, so we're okay examining "nextIdx".
+             */
+            assert(nextIdx < insnsSize);
+            endBB = true;
+        } else if (opcode == OP_NOP && isDataChunk(meth->insns[nextIdx])) {
+            /*
+             * Handle an odd special case: if this is NOP padding before a
+             * data chunk, also treat it as "ignore".  Otherwise it'll look
+             * like a block that starts and doesn't end.
+             */
+            endBB = ignoreInstr = true;
+        } else {
+            /* check: return ops should be caught by absence of can-continue */
+            assert((opFlags & kInstrCanReturn) == 0);
+        }
+
+        if (verbose) {
+            char btc = dvmInsnIsBranchTarget(insnFlags, idx) ? '>' : ' ';
+            char tryc =
+                (pTries != NULL && idx >= tryStart && idx < tryEnd) ? 't' : ' ';
+            bool startBB = (idx == blockStartAddr);
+            const char* startEnd;
+
+
+            if (ignoreInstr)
+                startEnd = "IGNORE";
+            else if (startBB && endBB)
+                startEnd = "START/END";
+            else if (startBB)
+                startEnd = "START";
+            else if (endBB)
+                startEnd = "END";
+            else
+                startEnd = "-";
+
+            LOGI("%04x: %c%c%s #%d\n", idx, tryc, btc, startEnd, debugBBIndex);
+
+            if (pTries != NULL && idx == tryStart) {
+                assert(numHandlers > 0);
+                LOGI("  EXC block: [%04x, %04x) %d:(%04x...)\n",
+                    tryStart, tryEnd, numHandlers, handlerList[0]);
+            }
+        }
+
+        if (idx != blockStartAddr) {
+            /* should not be a basic block struct associated with this addr */
+            assert(vdata->basicBlocks[idx] == NULL);
+        }
+        if (endBB) {
+            if (!ignoreInstr) {
+                /*
+                 * Create a new BB if one doesn't already exist.
+                 */
+                VfyBasicBlock* curBlock = vdata->basicBlocks[blockStartAddr];
+                if (curBlock == NULL) {
+                    curBlock = allocVfyBasicBlock(vdata, blockStartAddr);
+                    if (curBlock == NULL)
+                        return false;
+                    vdata->basicBlocks[blockStartAddr] = curBlock;
+                }
+
+                curBlock->firstAddr = blockStartAddr;
+                curBlock->lastAddr = idx;
+
+                if (!setPredecessors(vdata, curBlock, idx, opFlags, nextIdx,
+                        handlerList, numHandlers))
+                {
+                    goto bail;
+                }
+            }
+
+            blockStartAddr = nextIdx;
+            debugBBIndex++;
+        }
+
+        idx = nextIdx;
+    }
+
+    assert(idx == insnsSize);
+
+    result = true;
+
+    if (verbose)
+        dumpBasicBlocks(vdata);
+
+bail:
+    free(handlerListAlloc);
+    return result;
+}
+
+/*
+ * Free the storage used by basic blocks.
+ */
+void dvmFreeVfyBasicBlocks(VerifierData* vdata)
+{
+    unsigned int idx;
+
+    if (vdata->basicBlocks == NULL)
+        return;
+
+    for (idx = 0; idx < vdata->insnsSize; idx++) {
+        VfyBasicBlock* block = vdata->basicBlocks[idx];
+        if (block == NULL)
+            continue;
+
+        dvmPointerSetFree(block->predecessors);
+        free(block);
+    }
+}
diff --git a/vm/analysis/VfyBasicBlock.h b/vm/analysis/VfyBasicBlock.h
new file mode 100644
index 0000000..c2d6618
--- /dev/null
+++ b/vm/analysis/VfyBasicBlock.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Basic block functions, as used by the verifier.  (The names were chosen
+ * to avoid conflicts with similar structures used by the compiler.)
+ */
+#ifndef _DALVIK_VFYBASICBLOCK
+#define _DALVIK_VFYBASICBLOCK
+
+#include "libdex/InstrUtils.h"
+#include "PointerSet.h"
+
+struct VerifierData;
+
+
+/*
+ * Structure representing a basic block.
+ *
+ * This is used for liveness analysis, which is a reverse-flow algorithm,
+ * so we need to mantain a list of predecessors for each block.
+ */
+typedef struct {
+    u4              firstAddr;      /* address of first instruction */
+    u4              lastAddr;       /* address of last instruction */
+    PointerSet*     predecessors;   /* set of basic blocks that can flow here */
+} VfyBasicBlock;
+
+/*
+ * Generate a list of basic blocks.
+ */
+bool dvmComputeVfyBasicBlocks(struct VerifierData* vdata);
+
+/*
+ * Free storage allocated by dvmComputeVfyBasicBlocks.
+ */
+void dvmFreeVfyBasicBlocks(struct VerifierData* vdata);
+
+#endif /*_DALVIK_VFYBASICBLOCK*/